diff --git a/HW-3.1.ipynb b/HW-3.1.ipynb new file mode 100644 index 00000000..c05a3b71 --- /dev/null +++ b/HW-3.1.ipynb @@ -0,0 +1,4027 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "398a86d9", + "metadata": {}, + "outputs": [], + "source": [ + "from pprint import pprint\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "import sys\n", + "sys.path.append('../')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "8dbe6bf0", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "import requests\n", + "from tqdm.auto import tqdm\n", + "from scipy.stats import mode\n", + "from implicit.nearest_neighbours import CosineRecommender, TFIDFRecommender, BM25Recommender\n", + "from rectools import Columns\n", + "from rectools.model_selection import TimeRangeSplitter\n", + "from rectools.metrics import Precision, Recall, MAP, MeanInvUserFreq, Serendipity, calc_metrics\n", + "from rectools.dataset.interactions import Interactions\n", + "\n", + "from service.utils.user_knn import UserKnn" + ] + }, + { + "cell_type": "markdown", + "id": "b1baa79f", + "metadata": {}, + "source": [ + "# Data" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "f2a9e540", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((5476251, 5), (840197, 5), (15963, 14))" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions = pd.read_csv('../data/kion_train/interactions.csv')\n", + "users = pd.read_csv('../data/kion_train/users.csv')\n", + "items = pd.read_csv('../data/kion_train/items.csv')\n", + "\n", + "interactions.shape, users.shape, items.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "456d25f4", + "metadata": {}, + "outputs": [], + "source": [ + "interactions.rename(\n", + " columns={\n", + " 'last_watch_dt': Columns.Datetime,\n", + " 'total_dur': Columns.Weight\n", + " }, \n", + " inplace=True) \n", + "\n", + "interactions[Columns.Datetime] = pd.to_datetime(interactions[Columns.Datetime])" + ] + }, + { + "cell_type": "markdown", + "id": "6f7b9b0c", + "metadata": {}, + "source": [ + "## Intersection" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7c9c0c94", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_iddatetimeweightwatched_pct
017654995062021-05-11425072.0
169931716592021-05-298317100.0
265668371072021-05-09100.0
386461376382021-07-0514483100.0
496486895062021-04-306725100.0
5476246648596122252021-08-13760.0
547624754686296732021-04-13230849.0
5476248697262152972021-08-201830763.0
5476249384202161972021-04-196203100.0
547625031970944362021-08-15392145.0
\n", + "
" + ], + "text/plain": [ + " user_id item_id datetime weight watched_pct\n", + "0 176549 9506 2021-05-11 4250 72.0\n", + "1 699317 1659 2021-05-29 8317 100.0\n", + "2 656683 7107 2021-05-09 10 0.0\n", + "3 864613 7638 2021-07-05 14483 100.0\n", + "4 964868 9506 2021-04-30 6725 100.0\n", + "5476246 648596 12225 2021-08-13 76 0.0\n", + "5476247 546862 9673 2021-04-13 2308 49.0\n", + "5476248 697262 15297 2021-08-20 18307 63.0\n", + "5476249 384202 16197 2021-04-19 6203 100.0\n", + "5476250 319709 4436 2021-08-15 3921 45.0" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.concat([interactions.head(), interactions.tail()])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c5c3ce6c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Interactions dataframe shape: (5476251, 5)\n", + "Unique users in interactions: 962179\n", + "Unique items in interactions: 15706\n" + ] + } + ], + "source": [ + "print(f\"Interactions dataframe shape: {interactions.shape}\")\n", + "print(f\"Unique users in interactions: {interactions[Columns.User].nunique()}\")\n", + "print(f\"Unique items in interactions: {interactions[Columns.Item].nunique()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0214a978", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "min date in interactions: 2021-03-13 00:00:00\n", + "max date in interactions: 2021-08-22 00:00:00\n" + ] + } + ], + "source": [ + "max_date = interactions[Columns.Datetime].max()\n", + "min_date = interactions[Columns.Datetime].min()\n", + "\n", + "print(f\"min date in interactions: {min_date}\")\n", + "print(f\"max date in interactions: {max_date}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "7829e796", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 5476251 entries, 0 to 5476250\n", + "Data columns (total 5 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 user_id int64 \n", + " 1 item_id int64 \n", + " 2 datetime datetime64[ns]\n", + " 3 weight int64 \n", + " 4 watched_pct float64 \n", + "dtypes: datetime64[ns](1), float64(1), int64(3)\n", + "memory usage: 208.9 MB\n" + ] + } + ], + "source": [ + "interactions.info()" + ] + }, + { + "cell_type": "markdown", + "id": "57cddf34", + "metadata": {}, + "source": [ + "## Users" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "de5dea16", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_idageincomesexkids_flg
0973171age_25_34income_60_90М1
1962099age_18_24income_20_40М0
21047345age_45_54income_40_60Ж0
3721985age_45_54income_20_40Ж0
4704055age_35_44income_60_90Ж0
840192339025age_65_infincome_0_20Ж0
840193983617age_18_24income_20_40Ж1
840194251008NaNNaNNaN0
840195590706NaNNaNЖ0
840196166555age_65_infincome_20_40Ж0
\n", + "
" + ], + "text/plain": [ + " user_id age income sex kids_flg\n", + "0 973171 age_25_34 income_60_90 М 1\n", + "1 962099 age_18_24 income_20_40 М 0\n", + "2 1047345 age_45_54 income_40_60 Ж 0\n", + "3 721985 age_45_54 income_20_40 Ж 0\n", + "4 704055 age_35_44 income_60_90 Ж 0\n", + "840192 339025 age_65_inf income_0_20 Ж 0\n", + "840193 983617 age_18_24 income_20_40 Ж 1\n", + "840194 251008 NaN NaN NaN 0\n", + "840195 590706 NaN NaN Ж 0\n", + "840196 166555 age_65_inf income_20_40 Ж 0" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.concat([users.head(), users.tail()])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "e4e6d2f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Users dataframe shape (840197, 5)\n", + "Unique users: 840197\n" + ] + } + ], + "source": [ + "print(f\"Users dataframe shape {users.shape}\")\n", + "print(f\"Unique users: {users['user_id'].nunique()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "98b4ff6c", + "metadata": {}, + "source": [ + "## Items" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "19b43ff0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_typetitletitle_origrelease_yeargenrescountriesfor_kidsage_ratingstudiosdirectorsactorsdescriptionkeywords
010711filmПоговори с нейHable con ella2002.0драмы, зарубежные, детективы, мелодрамыИспанияNaN16.0NaNПедро АльмодоварАдольфо Фернандес, Ана Фернандес, Дарио Гранди...Мелодрама легендарного Педро Альмодовара «Пого...Поговори, ней, 2002, Испания, друзья, любовь, ...
12508filmГолые перцыSearch Party2014.0зарубежные, приключения, комедииСШАNaN16.0NaNСкот АрмстронгАдам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ...Уморительная современная комедия на популярную...Голые, перцы, 2014, США, друзья, свадьбы, прео...
159614538seriesСреди камнейDarklands2019.0драмы, спорт, криминалРоссия0.018.0NaNМарк О’Коннор, Конор МакМахонДэйн Уайт О’Хара, Томас Кэйн-Бирн, Джудит Родд...Семнадцатилетний Дэмиен мечтает вырваться за п...Среди, камней, 2019, Россия
159623206seriesГошаNaN2019.0комедииРоссия0.016.0NaNМихаил МироновМкртыч Арзуманян, Виктория РунцоваДобродушный Гоша не может выйти из дома, чтобы...Гоша, 2019, Россия
\n", + "
" + ], + "text/plain": [ + " item_id content_type title title_orig release_year \\\n", + "0 10711 film Поговори с ней Hable con ella 2002.0 \n", + "1 2508 film Голые перцы Search Party 2014.0 \n", + "15961 4538 series Среди камней Darklands 2019.0 \n", + "15962 3206 series Гоша NaN 2019.0 \n", + "\n", + " genres countries for_kids \\\n", + "0 драмы, зарубежные, детективы, мелодрамы Испания NaN \n", + "1 зарубежные, приключения, комедии США NaN \n", + "15961 драмы, спорт, криминал Россия 0.0 \n", + "15962 комедии Россия 0.0 \n", + "\n", + " age_rating studios directors \\\n", + "0 16.0 NaN Педро Альмодовар \n", + "1 16.0 NaN Скот Армстронг \n", + "15961 18.0 NaN Марк О’Коннор, Конор МакМахон \n", + "15962 16.0 NaN Михаил Миронов \n", + "\n", + " actors \\\n", + "0 Адольфо Фернандес, Ана Фернандес, Дарио Гранди... \n", + "1 Адам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ... \n", + "15961 Дэйн Уайт О’Хара, Томас Кэйн-Бирн, Джудит Родд... \n", + "15962 Мкртыч Арзуманян, Виктория Рунцова \n", + "\n", + " description \\\n", + "0 Мелодрама легендарного Педро Альмодовара «Пого... \n", + "1 Уморительная современная комедия на популярную... \n", + "15961 Семнадцатилетний Дэмиен мечтает вырваться за п... \n", + "15962 Добродушный Гоша не может выйти из дома, чтобы... \n", + "\n", + " keywords \n", + "0 Поговори, ней, 2002, Испания, друзья, любовь, ... \n", + "1 Голые, перцы, 2014, США, друзья, свадьбы, прео... \n", + "15961 Среди, камней, 2019, Россия \n", + "15962 Гоша, 2019, Россия " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.concat([items.head(2), items.tail(2)])" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "8c8fb319", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Items dataframe shape (15963, 14)\n", + "Unique item_id: 15963\n" + ] + } + ], + "source": [ + "print(f\"Items dataframe shape {items.shape}\")\n", + "print(f\"Unique item_id: {items['item_id'].nunique()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2b35b460", + "metadata": {}, + "source": [ + "# userkNN model CV" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f60e6ecb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "alignmentgroup": "True", + "hovertemplate": "variable=user_id
datetime=%{x}
value=%{y}", + "legendgroup": "user_id", + "marker": { + "color": "#636efa", + "pattern": { + "shape": "" + } + }, + "name": "user_id", + "offsetgroup": "user_id", + "orientation": "v", + "showlegend": true, + "textposition": "auto", + "type": "bar", + "x": [ + "2021-03-13T00:00:00", + "2021-03-14T00:00:00", + "2021-03-15T00:00:00", + "2021-03-16T00:00:00", + "2021-03-17T00:00:00", + "2021-03-18T00:00:00", + "2021-03-19T00:00:00", + "2021-03-20T00:00:00", + "2021-03-21T00:00:00", + "2021-03-22T00:00:00", + "2021-03-23T00:00:00", + "2021-03-24T00:00:00", + "2021-03-25T00:00:00", + "2021-03-26T00:00:00", + "2021-03-27T00:00:00", + "2021-03-28T00:00:00", + "2021-03-29T00:00:00", + "2021-03-30T00:00:00", + "2021-03-31T00:00:00", + "2021-04-01T00:00:00", + "2021-04-02T00:00:00", + "2021-04-03T00:00:00", + "2021-04-04T00:00:00", + "2021-04-05T00:00:00", + "2021-04-06T00:00:00", + "2021-04-07T00:00:00", + "2021-04-08T00:00:00", + "2021-04-09T00:00:00", + "2021-04-10T00:00:00", + "2021-04-11T00:00:00", + "2021-04-12T00:00:00", + "2021-04-13T00:00:00", + "2021-04-14T00:00:00", + "2021-04-15T00:00:00", + "2021-04-16T00:00:00", + "2021-04-17T00:00:00", + "2021-04-18T00:00:00", + "2021-04-19T00:00:00", + "2021-04-20T00:00:00", + "2021-04-21T00:00:00", + "2021-04-22T00:00:00", + "2021-04-23T00:00:00", + "2021-04-24T00:00:00", + "2021-04-25T00:00:00", + "2021-04-26T00:00:00", + "2021-04-27T00:00:00", + "2021-04-28T00:00:00", + "2021-04-29T00:00:00", + "2021-04-30T00:00:00", + "2021-05-01T00:00:00", + "2021-05-02T00:00:00", + "2021-05-03T00:00:00", + "2021-05-04T00:00:00", + "2021-05-05T00:00:00", + "2021-05-06T00:00:00", + "2021-05-07T00:00:00", + "2021-05-08T00:00:00", + "2021-05-09T00:00:00", + "2021-05-10T00:00:00", + "2021-05-11T00:00:00", + "2021-05-12T00:00:00", + "2021-05-13T00:00:00", + "2021-05-14T00:00:00", + "2021-05-15T00:00:00", + "2021-05-16T00:00:00", + "2021-05-17T00:00:00", + "2021-05-18T00:00:00", + "2021-05-19T00:00:00", + "2021-05-20T00:00:00", + "2021-05-21T00:00:00", + "2021-05-22T00:00:00", + "2021-05-23T00:00:00", + "2021-05-24T00:00:00", + "2021-05-25T00:00:00", + "2021-05-26T00:00:00", + "2021-05-27T00:00:00", + "2021-05-28T00:00:00", + "2021-05-29T00:00:00", + "2021-05-30T00:00:00", + "2021-05-31T00:00:00", + "2021-06-01T00:00:00", + "2021-06-02T00:00:00", + "2021-06-03T00:00:00", + "2021-06-04T00:00:00", + "2021-06-05T00:00:00", + "2021-06-06T00:00:00", + "2021-06-07T00:00:00", + "2021-06-08T00:00:00", + "2021-06-09T00:00:00", + "2021-06-10T00:00:00", + "2021-06-11T00:00:00", + "2021-06-12T00:00:00", + "2021-06-13T00:00:00", + "2021-06-14T00:00:00", + "2021-06-15T00:00:00", + "2021-06-16T00:00:00", + "2021-06-17T00:00:00", + "2021-06-18T00:00:00", + "2021-06-19T00:00:00", + "2021-06-20T00:00:00", + "2021-06-21T00:00:00", + "2021-06-22T00:00:00", + "2021-06-23T00:00:00", + "2021-06-24T00:00:00", + "2021-06-25T00:00:00", + "2021-06-26T00:00:00", + "2021-06-27T00:00:00", + "2021-06-28T00:00:00", + "2021-06-29T00:00:00", + "2021-06-30T00:00:00", + "2021-07-01T00:00:00", + "2021-07-02T00:00:00", + "2021-07-03T00:00:00", + "2021-07-04T00:00:00", + "2021-07-05T00:00:00", + "2021-07-06T00:00:00", + "2021-07-07T00:00:00", + "2021-07-08T00:00:00", + "2021-07-09T00:00:00", + "2021-07-10T00:00:00", + "2021-07-11T00:00:00", + "2021-07-12T00:00:00", + "2021-07-13T00:00:00", + "2021-07-14T00:00:00", + "2021-07-15T00:00:00", + "2021-07-16T00:00:00", + "2021-07-17T00:00:00", + "2021-07-18T00:00:00", + "2021-07-19T00:00:00", + "2021-07-20T00:00:00", + "2021-07-21T00:00:00", + "2021-07-22T00:00:00", + "2021-07-23T00:00:00", + "2021-07-24T00:00:00", + "2021-07-25T00:00:00", + "2021-07-26T00:00:00", + "2021-07-27T00:00:00", + "2021-07-28T00:00:00", + "2021-07-29T00:00:00", + "2021-07-30T00:00:00", + "2021-07-31T00:00:00", + "2021-08-01T00:00:00", + "2021-08-02T00:00:00", + "2021-08-03T00:00:00", + "2021-08-04T00:00:00", + "2021-08-05T00:00:00", + "2021-08-06T00:00:00", + "2021-08-07T00:00:00", + "2021-08-08T00:00:00", + "2021-08-09T00:00:00", + "2021-08-10T00:00:00", + "2021-08-11T00:00:00", + "2021-08-12T00:00:00", + "2021-08-13T00:00:00", + "2021-08-14T00:00:00", + "2021-08-15T00:00:00", + "2021-08-16T00:00:00", + "2021-08-17T00:00:00", + "2021-08-18T00:00:00", + "2021-08-19T00:00:00", + "2021-08-20T00:00:00", + "2021-08-21T00:00:00", + "2021-08-22T00:00:00" + ], + "xaxis": "x", + "y": [ + 16104, + 15606, + 12363, + 12643, + 12753, + 12788, + 13657, + 15346, + 15560, + 12752, + 13147, + 13435, + 12698, + 13909, + 15657, + 16112, + 12783, + 13101, + 13460, + 12966, + 14084, + 15431, + 15346, + 12642, + 12528, + 13129, + 13827, + 14416, + 15937, + 16046, + 12835, + 12322, + 12451, + 12275, + 13342, + 15464, + 16275, + 14286, + 20420, + 23200, + 21274, + 22127, + 26161, + 28964, + 21625, + 22590, + 21406, + 19987, + 21406, + 23479, + 24767, + 26267, + 25983, + 23941, + 23510, + 23201, + 27550, + 25986, + 27242, + 20957, + 20578, + 20729, + 21152, + 24530, + 24914, + 20960, + 20574, + 21561, + 22712, + 25697, + 27895, + 29978, + 24317, + 23667, + 22529, + 23881, + 24131, + 29035, + 31308, + 26821, + 26587, + 27577, + 28683, + 33150, + 34795, + 37096, + 31402, + 31107, + 32896, + 38964, + 37935, + 38619, + 42125, + 38973, + 35993, + 57686, + 41440, + 42174, + 43679, + 47989, + 39127, + 39693, + 41688, + 38394, + 41428, + 45898, + 48903, + 43301, + 43887, + 67749, + 53900, + 46642, + 48832, + 52812, + 43375, + 41380, + 41163, + 41592, + 40955, + 44798, + 46250, + 42487, + 43764, + 43128, + 43010, + 44878, + 49714, + 54139, + 45541, + 44431, + 44422, + 46313, + 46911, + 50317, + 54378, + 48531, + 49324, + 50267, + 50585, + 53121, + 59499, + 62128, + 53495, + 52181, + 51911, + 51047, + 53745, + 59316, + 61454, + 52794, + 53712, + 55617, + 56497, + 55843, + 61644, + 66546, + 54546, + 54311, + 56789, + 58640, + 60145, + 68834, + 71171 + ], + "yaxis": "y" + } + ], + "layout": { + "barmode": "relative", + "legend": { + "title": { + "text": "variable" + }, + "tracegroupgap": 0 + }, + "margin": { + "t": 60 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": { + "text": "datetime" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "title": { + "text": "value" + } + } + } + }, + "text/html": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = px.bar(interactions.groupby(Columns.Datetime)[Columns.User].agg('count'))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "43f216d0", + "metadata": {}, + "source": [ + "Из графика видны **недельные тенденции** просмотров, поэтому следует fold-ы разделять по 7 дней, но т.к. на семинаре дали \"намек\", что private dataset имеет количество дней, меньшее чем 7. Поэтому фолды будут разбиваться на **5 и 7 дней**" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "07fbdb30", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.to_datetime('23-05-2021', format='%d-%m-%Y').weekday()" + ] + }, + { + "cell_type": "markdown", + "id": "2ff625b2", + "metadata": {}, + "source": [ + "### train test split" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "759ba346", + "metadata": {}, + "outputs": [], + "source": [ + "def create_data_range(\n", + " last_date: pd.Timestamp, \n", + " n_folds: int = 7, \n", + " unit: str = \"W\", \n", + " n_units: int = 1, \n", + " show: bool = True,\n", + "):\n", + " periods = n_folds + 1\n", + " freq = f\"{n_units}{unit}\"\n", + " \n", + " start_date = last_date - pd.Timedelta(n_folds * n_units + n_units, unit=unit) \n", + " \n", + " date_range = pd.date_range(start=start_date, periods=periods, freq=freq, tz=last_date.tz)\n", + " \n", + " if show:\n", + " print(\n", + " f\"start_date: {start_date}\\n\"\n", + " f\"last_date: {last_date}\\n\"\n", + " f\"periods: {periods}\\n\"\n", + " f\"freq: {freq}\\n\"\n", + " f\"Test fold borders: {date_range.values.astype('datetime64[D]')}\\n\"\n", + " )\n", + " \n", + " return date_range" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "38bfd397", + "metadata": {}, + "outputs": [], + "source": [ + "CONFIG_CV = {\n", + " \"cv_v1\": {\n", + " \"n_folds\": 7,\n", + " \"unit\": \"W\",\n", + " \"n_units\": 1,\n", + " },\n", + " \"cv_v2\": {\n", + " \"n_folds\": 7,\n", + " \"unit\": \"D\",\n", + " \"n_units\": 5,\n", + " }, \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f518e089", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Timestamp('2021-08-22 00:00:00')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "last_date = interactions[Columns.Datetime].max().normalize()\n", + "last_date" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1fd68b9b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "***Folds v1***\n", + "start_date: 2021-07-13 00:00:00\n", + "last_date: 2021-08-22 00:00:00\n", + "periods: 8\n", + "freq: 5D\n", + "Test fold borders: ['2021-07-13' '2021-07-18' '2021-07-23' '2021-07-28' '2021-08-02'\n", + " '2021-08-07' '2021-08-12' '2021-08-17']\n", + "\n" + ] + } + ], + "source": [ + "print(\"***Folds v1***\")\n", + "date_range_v1 = create_data_range(\n", + " last_date, \n", + " n_folds=CONFIG_CV[\"cv_v2\"][\"n_folds\"], \n", + " unit=CONFIG_CV[\"cv_v2\"][\"unit\"], \n", + " n_units=CONFIG_CV[\"cv_v2\"][\"n_units\"]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "efc59555", + "metadata": {}, + "source": [ + "**генерируем фолды** " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9fae43f6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Real number of folds: 7\n" + ] + } + ], + "source": [ + "cv_v1 = TimeRangeSplitter(\n", + " date_range=date_range_v1,\n", + " filter_already_seen=True,\n", + " filter_cold_items=True,\n", + " filter_cold_users=True,\n", + ")\n", + "print(f\"Real number of folds: {cv_v1.get_n_splits(Interactions(interactions))}\")\n", + "\n", + "CV = [cv_v1]" + ] + }, + { + "cell_type": "markdown", + "id": "e15a83a7", + "metadata": {}, + "source": [ + "**Формируем метрики**" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8f7742c6", + "metadata": {}, + "outputs": [], + "source": [ + "metrics = {\n", + " \"prec@10\": Precision(k=10),\n", + " \"recall@10\": Recall(k=10),\n", + " \"MAP@10\": MAP(k=10),\n", + " \"novelty\": MeanInvUserFreq(k=10),\n", + " \"serendipity\": Serendipity(k=10),\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b21a1ecf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'cosine_userknn_K30': ,\n", + " 'tfidf_userknn_K30': ,\n", + " 'bm25_userknn_K30': ,\n", + " 'cosine_userknn_K40': ,\n", + " 'tfidf_userknn_K40': ,\n", + " 'bm25_userknn_K40': }" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "K = [30, 40]\n", + "models = dict()\n", + "\n", + "for k in K:\n", + " models[f\"cosine_userknn_K{k}\"] = CosineRecommender(K=k)\n", + " models[f\"tfidf_userknn_K{k}\"] = TFIDFRecommender(K=k)\n", + " models[f\"bm25_userknn_K{k}\"] = BM25Recommender(K=k)\n", + "\n", + "models" + ] + }, + { + "cell_type": "markdown", + "id": "0103149a", + "metadata": {}, + "source": [ + "## Training" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "e78b8221", + "metadata": {}, + "outputs": [], + "source": [ + "N_USERS = 50" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "50dcff0b", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "\n", + "results = []\n", + "\n", + "for idx, cv in enumerate(CV):\n", + " print(f\"\\n CV version {idx}\")\n", + " fold_iterator = cv.split(Interactions(interactions), collect_fold_stats=True)\n", + "\n", + " for i_fold, (train_ids, test_ids, fold_info) in enumerate(fold_iterator):\n", + " print(f\"\\n==================== Fold {i_fold}\")\n", + " pprint(fold_info)\n", + "\n", + " df_train = interactions.iloc[train_ids].copy()\n", + " df_test = interactions.iloc[test_ids][Columns.UserItem].copy()\n", + "\n", + " catalog = df_train[Columns.Item].unique()\n", + "\n", + " for model_name, model in models.items():\n", + " userknn_model = UserKnn(model=model, N_users=N_USERS, use_weight_idf=True)\n", + " userknn_model.fit(df_train)\n", + "\n", + " if 'bm25' in model_name:\n", + " recos = userknn_model.predict(df_test, bmp25=True)\n", + " else:\n", + " recos = userknn_model.predict(df_test)\n", + "\n", + " metric_values = calc_metrics(\n", + " metrics,\n", + " reco=recos,\n", + " interactions=df_test,\n", + " prev_interactions=df_train,\n", + " catalog=catalog,\n", + " )\n", + "\n", + " full_model_name = f\"{model_name}_cv-{idx}\"\n", + " fold = {\"fold\": i_fold, \"model\": full_model_name}\n", + " fold.update(metric_values)\n", + " results.append(fold)" + ] + }, + { + "cell_type": "markdown", + "id": "708ec5c2", + "metadata": {}, + "source": [ + "Работало больше 10 часов, случайно при перезапуске ноутбука была вызвана ячейка и остановлена, поэтому завершилась с ошибкой, поэтому ошибку убрали для лучшего вида" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "d7e2ffa7", + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
foldmodelprec@10recall@10MAP@10noveltyserendipity
00cosine_userknn_K30_cv-00.0035570.0211280.0036958.3314910.000040
10tfidf_userknn_K30_cv-00.0064390.0391020.0073358.1550510.000048
20bm25_userknn_K30_cv-00.0025930.0134940.0025319.3984670.000081
30cosine_userknn_K40_cv-00.0032820.0193230.0034018.5615230.000043
40tfidf_userknn_K40_cv-00.0061780.0374580.0069578.3004040.000052
50bm25_userknn_K40_cv-00.0022410.0112550.0022109.6755330.000081
61cosine_userknn_K30_cv-00.0035050.0200020.0035808.3982480.000046
71tfidf_userknn_K30_cv-00.0063280.0368440.0070228.2401330.000058
81bm25_userknn_K30_cv-00.0027220.0138560.0026589.4846920.000088
91cosine_userknn_K40_cv-00.0032450.0183680.0033058.6269060.000047
101tfidf_userknn_K40_cv-00.0061500.0359640.0069168.3779880.000061
111bm25_userknn_K40_cv-00.0024060.0120670.0023939.7564580.000086
122cosine_userknn_K30_cv-00.0032610.0184980.0032958.4392630.000047
132tfidf_userknn_K30_cv-00.0059400.0342330.0064798.2623670.000059
142bm25_userknn_K30_cv-00.0027200.0134220.0025309.5356310.000091
152cosine_userknn_K40_cv-00.0030450.0170860.0031008.6615850.000050
162tfidf_userknn_K40_cv-00.0059140.0340710.0064398.3966180.000063
172bm25_userknn_K40_cv-00.0024040.0116380.0022319.7991190.000090
183cosine_userknn_K30_cv-00.0032770.0187860.0033958.4449860.000045
193tfidf_userknn_K30_cv-00.0060230.0341710.0063288.2765030.000059
203bm25_userknn_K30_cv-00.0026200.0127620.0024979.5609840.000091
213cosine_userknn_K40_cv-00.0030760.0175120.0031738.6581500.000045
223tfidf_userknn_K40_cv-00.0059190.0333680.0062538.3991690.000062
233bm25_userknn_K40_cv-00.0023370.0112730.0022539.8163250.000089
244cosine_userknn_K30_cv-00.0031180.0180640.0031578.4858990.000042
254tfidf_userknn_K30_cv-00.0059110.0336260.0063968.2824280.000059
264bm25_userknn_K30_cv-00.0025370.0123680.0024709.5996450.000086
274cosine_userknn_K40_cv-00.0028720.0165090.0028838.7119840.000043
284tfidf_userknn_K40_cv-00.0057930.0330280.0062618.4166800.000062
294bm25_userknn_K40_cv-00.0022130.0108600.0021799.8662010.000085
305cosine_userknn_K30_cv-00.0030030.0162520.0028998.4989680.000043
315tfidf_userknn_K30_cv-00.0055270.0309420.0058238.3252730.000057
325bm25_userknn_K30_cv-00.0025970.0122630.0023869.6469570.000100
335cosine_userknn_K40_cv-00.0027650.0147130.0026618.7175590.000047
345tfidf_userknn_K40_cv-00.0055450.0308920.0058178.4540910.000059
355bm25_userknn_K40_cv-00.0023020.0107770.0021359.9140420.000100
366cosine_userknn_K30_cv-00.0029630.0165320.0028878.5638090.000050
376tfidf_userknn_K30_cv-00.0053300.0307170.0057638.3662590.000064
386bm25_userknn_K30_cv-00.0025710.0126910.0024789.7150970.000100
396cosine_userknn_K40_cv-00.0027690.0154480.0026758.7750580.000051
406tfidf_userknn_K40_cv-00.0052840.0304180.0056978.4884730.000066
416bm25_userknn_K40_cv-00.0023400.0112780.0022089.9646640.000099
\n", + "
" + ], + "text/plain": [ + " fold model prec@10 recall@10 MAP@10 novelty \\\n", + "0 0 cosine_userknn_K30_cv-0 0.003557 0.021128 0.003695 8.331491 \n", + "1 0 tfidf_userknn_K30_cv-0 0.006439 0.039102 0.007335 8.155051 \n", + "2 0 bm25_userknn_K30_cv-0 0.002593 0.013494 0.002531 9.398467 \n", + "3 0 cosine_userknn_K40_cv-0 0.003282 0.019323 0.003401 8.561523 \n", + "4 0 tfidf_userknn_K40_cv-0 0.006178 0.037458 0.006957 8.300404 \n", + "5 0 bm25_userknn_K40_cv-0 0.002241 0.011255 0.002210 9.675533 \n", + "6 1 cosine_userknn_K30_cv-0 0.003505 0.020002 0.003580 8.398248 \n", + "7 1 tfidf_userknn_K30_cv-0 0.006328 0.036844 0.007022 8.240133 \n", + "8 1 bm25_userknn_K30_cv-0 0.002722 0.013856 0.002658 9.484692 \n", + "9 1 cosine_userknn_K40_cv-0 0.003245 0.018368 0.003305 8.626906 \n", + "10 1 tfidf_userknn_K40_cv-0 0.006150 0.035964 0.006916 8.377988 \n", + "11 1 bm25_userknn_K40_cv-0 0.002406 0.012067 0.002393 9.756458 \n", + "12 2 cosine_userknn_K30_cv-0 0.003261 0.018498 0.003295 8.439263 \n", + "13 2 tfidf_userknn_K30_cv-0 0.005940 0.034233 0.006479 8.262367 \n", + "14 2 bm25_userknn_K30_cv-0 0.002720 0.013422 0.002530 9.535631 \n", + "15 2 cosine_userknn_K40_cv-0 0.003045 0.017086 0.003100 8.661585 \n", + "16 2 tfidf_userknn_K40_cv-0 0.005914 0.034071 0.006439 8.396618 \n", + "17 2 bm25_userknn_K40_cv-0 0.002404 0.011638 0.002231 9.799119 \n", + "18 3 cosine_userknn_K30_cv-0 0.003277 0.018786 0.003395 8.444986 \n", + "19 3 tfidf_userknn_K30_cv-0 0.006023 0.034171 0.006328 8.276503 \n", + "20 3 bm25_userknn_K30_cv-0 0.002620 0.012762 0.002497 9.560984 \n", + "21 3 cosine_userknn_K40_cv-0 0.003076 0.017512 0.003173 8.658150 \n", + "22 3 tfidf_userknn_K40_cv-0 0.005919 0.033368 0.006253 8.399169 \n", + "23 3 bm25_userknn_K40_cv-0 0.002337 0.011273 0.002253 9.816325 \n", + "24 4 cosine_userknn_K30_cv-0 0.003118 0.018064 0.003157 8.485899 \n", + "25 4 tfidf_userknn_K30_cv-0 0.005911 0.033626 0.006396 8.282428 \n", + "26 4 bm25_userknn_K30_cv-0 0.002537 0.012368 0.002470 9.599645 \n", + "27 4 cosine_userknn_K40_cv-0 0.002872 0.016509 0.002883 8.711984 \n", + "28 4 tfidf_userknn_K40_cv-0 0.005793 0.033028 0.006261 8.416680 \n", + "29 4 bm25_userknn_K40_cv-0 0.002213 0.010860 0.002179 9.866201 \n", + "30 5 cosine_userknn_K30_cv-0 0.003003 0.016252 0.002899 8.498968 \n", + "31 5 tfidf_userknn_K30_cv-0 0.005527 0.030942 0.005823 8.325273 \n", + "32 5 bm25_userknn_K30_cv-0 0.002597 0.012263 0.002386 9.646957 \n", + "33 5 cosine_userknn_K40_cv-0 0.002765 0.014713 0.002661 8.717559 \n", + "34 5 tfidf_userknn_K40_cv-0 0.005545 0.030892 0.005817 8.454091 \n", + "35 5 bm25_userknn_K40_cv-0 0.002302 0.010777 0.002135 9.914042 \n", + "36 6 cosine_userknn_K30_cv-0 0.002963 0.016532 0.002887 8.563809 \n", + "37 6 tfidf_userknn_K30_cv-0 0.005330 0.030717 0.005763 8.366259 \n", + "38 6 bm25_userknn_K30_cv-0 0.002571 0.012691 0.002478 9.715097 \n", + "39 6 cosine_userknn_K40_cv-0 0.002769 0.015448 0.002675 8.775058 \n", + "40 6 tfidf_userknn_K40_cv-0 0.005284 0.030418 0.005697 8.488473 \n", + "41 6 bm25_userknn_K40_cv-0 0.002340 0.011278 0.002208 9.964664 \n", + "\n", + " serendipity \n", + "0 0.000040 \n", + "1 0.000048 \n", + "2 0.000081 \n", + "3 0.000043 \n", + "4 0.000052 \n", + "5 0.000081 \n", + "6 0.000046 \n", + "7 0.000058 \n", + "8 0.000088 \n", + "9 0.000047 \n", + "10 0.000061 \n", + "11 0.000086 \n", + "12 0.000047 \n", + "13 0.000059 \n", + "14 0.000091 \n", + "15 0.000050 \n", + "16 0.000063 \n", + "17 0.000090 \n", + "18 0.000045 \n", + "19 0.000059 \n", + "20 0.000091 \n", + "21 0.000045 \n", + "22 0.000062 \n", + "23 0.000089 \n", + "24 0.000042 \n", + "25 0.000059 \n", + "26 0.000086 \n", + "27 0.000043 \n", + "28 0.000062 \n", + "29 0.000085 \n", + "30 0.000043 \n", + "31 0.000057 \n", + "32 0.000100 \n", + "33 0.000047 \n", + "34 0.000059 \n", + "35 0.000100 \n", + "36 0.000050 \n", + "37 0.000064 \n", + "38 0.000100 \n", + "39 0.000051 \n", + "40 0.000066 \n", + "41 0.000099 " + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_metrics = pd.DataFrame(results)\n", + "df_metrics" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "a0334b9a", + "metadata": {}, + "outputs": [], + "source": [ + "df_metrics.to_pickle(\"../data/hw_3/df_metrics.pickle\")" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "446530ce", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
foldprec@10recall@10MAP@10noveltyserendipity
model
bm25_userknn_K30_cv-03.00.0026230.0129800.0025079.5630680.000091
bm25_userknn_K40_cv-03.00.0023200.0113070.0022309.8274770.000090
cosine_userknn_K30_cv-03.00.0032410.0184660.0032728.4518090.000045
cosine_userknn_K40_cv-03.00.0030080.0169940.0030288.6732520.000047
tfidf_userknn_K30_cv-03.00.0059280.0342340.0064498.2725730.000058
tfidf_userknn_K40_cv-03.00.0058260.0336000.0063348.4047750.000061
\n", + "
" + ], + "text/plain": [ + " fold prec@10 recall@10 MAP@10 novelty \\\n", + "model \n", + "bm25_userknn_K30_cv-0 3.0 0.002623 0.012980 0.002507 9.563068 \n", + "bm25_userknn_K40_cv-0 3.0 0.002320 0.011307 0.002230 9.827477 \n", + "cosine_userknn_K30_cv-0 3.0 0.003241 0.018466 0.003272 8.451809 \n", + "cosine_userknn_K40_cv-0 3.0 0.003008 0.016994 0.003028 8.673252 \n", + "tfidf_userknn_K30_cv-0 3.0 0.005928 0.034234 0.006449 8.272573 \n", + "tfidf_userknn_K40_cv-0 3.0 0.005826 0.033600 0.006334 8.404775 \n", + "\n", + " serendipity \n", + "model \n", + "bm25_userknn_K30_cv-0 0.000091 \n", + "bm25_userknn_K40_cv-0 0.000090 \n", + "cosine_userknn_K30_cv-0 0.000045 \n", + "cosine_userknn_K40_cv-0 0.000047 \n", + "tfidf_userknn_K30_cv-0 0.000058 \n", + "tfidf_userknn_K40_cv-0 0.000061 " + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_metrics.groupby('model').mean()" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "5fb9ba9f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prec@10recall@10MAP@10noveltyserendipity
model
bm25_userknn_K30_cv-00.0000720.0006120.0000830.1044680.000007
bm25_userknn_K40_cv-00.0000740.0004420.0000810.0973590.000007
cosine_userknn_K30_cv-00.0002310.0017490.0003140.0746990.000003
cosine_userknn_K40_cv-00.0002130.0016030.0002950.0693100.000003
tfidf_userknn_K30_cv-00.0003980.0030030.0005770.0666270.000005
tfidf_userknn_K40_cv-00.0003210.0025340.0004870.0595650.000004
\n", + "
" + ], + "text/plain": [ + " prec@10 recall@10 MAP@10 novelty serendipity\n", + "model \n", + "bm25_userknn_K30_cv-0 0.000072 0.000612 0.000083 0.104468 0.000007\n", + "bm25_userknn_K40_cv-0 0.000074 0.000442 0.000081 0.097359 0.000007\n", + "cosine_userknn_K30_cv-0 0.000231 0.001749 0.000314 0.074699 0.000003\n", + "cosine_userknn_K40_cv-0 0.000213 0.001603 0.000295 0.069310 0.000003\n", + "tfidf_userknn_K30_cv-0 0.000398 0.003003 0.000577 0.066627 0.000005\n", + "tfidf_userknn_K40_cv-0 0.000321 0.002534 0.000487 0.059565 0.000004" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_metrics.groupby('model').std()[metrics.keys()]" + ] + }, + { + "cell_type": "markdown", + "id": "41828ee5", + "metadata": {}, + "source": [ + "по **ofline** метрикам лучше всего себя показывает модель TFIDFRecommender\n", + "TFIDFRecommender подбор К" + ] + }, + { + "cell_type": "markdown", + "id": "7a8a0a41", + "metadata": {}, + "source": [ + "# Подбор оптимального K для TFIDFRecommender" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "1e91892d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'tfidf_userknn_K50': ,\n", + " 'tfidf_userknn_K60': ,\n", + " 'tfidf_userknn_K70': }" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "N_USERS = 50\n", + "\n", + "# Т.к. метрики для К 30 и 40 уже есть\n", + "K = [k for k in range(50, 71, 10)]\n", + "models = dict()\n", + "\n", + "for k in K:\n", + " models[f\"tfidf_userknn_K{k}\"] = TFIDFRecommender(K=k)\n", + "models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7c2c43b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "==================== Fold 0\n", + "{'End date': Timestamp('2021-07-18 00:00:00', freq='5D'),\n", + " 'Start date': Timestamp('2021-07-13 00:00:00', freq='5D'),\n", + " 'Test': 156580,\n", + " 'Test items': 5793,\n", + " 'Test users': 68150,\n", + " 'Train': 3281612,\n", + " 'Train items': 14754,\n", + " 'Train users': 652905}\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "211234f034a54bae86b94dff33b9f5c4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/652905 [00:00\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pct
017654995062021-05-11425072.0
169931716592021-05-298317100.0
265668371072021-05-09100.0
386461376382021-07-0514483100.0
496486895062021-04-306725100.0
5476246648596122252021-08-13760.0
547624754686296732021-04-13230849.0
5476248697262152972021-08-201830763.0
5476249384202161972021-04-196203100.0
547625031970944362021-08-15392145.0
\n", + "" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct\n", + "0 176549 9506 2021-05-11 4250 72.0\n", + "1 699317 1659 2021-05-29 8317 100.0\n", + "2 656683 7107 2021-05-09 10 0.0\n", + "3 864613 7638 2021-07-05 14483 100.0\n", + "4 964868 9506 2021-04-30 6725 100.0\n", + "5476246 648596 12225 2021-08-13 76 0.0\n", + "5476247 546862 9673 2021-04-13 2308 49.0\n", + "5476248 697262 15297 2021-08-20 18307 63.0\n", + "5476249 384202 16197 2021-04-19 6203 100.0\n", + "5476250 319709 4436 2021-08-15 3921 45.0" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.concat([interactions.head(), interactions.tail()])" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "dc4d9fd7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(962179,)" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions['user_id'].unique().shape" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "b7861d19", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(961833, 1.0),\n", + " (961849, 1.0),\n", + " (961857, 1.0),\n", + " (961871, 1.0),\n", + " (961873, 1.0),\n", + " (961876, 1.0),\n", + " (961887, 1.0),\n", + " (961907, 1.0),\n", + " (961910, 1.0),\n", + " (961912, 1.0)]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import dill\n", + "\n", + "with open('../service/weights/userKNN/userknn_tfidf_k30.dill', 'rb') as f:\n", + " userknn = dill.load(f)\n", + "\n", + "userknn.similar_items(962178, 10)" + ] + }, + { + "cell_type": "markdown", + "id": "1905033a", + "metadata": {}, + "source": [ + "# Popular Model" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "2df74dba", + "metadata": {}, + "outputs": [], + "source": [ + "from rectools.models import PopularModel\n", + "from rectools.dataset import Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "6ba37a73", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Timestamp('2021-08-22 00:00:00')" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "max_date = interactions[Columns.Datetime].max().normalize()\n", + "max_date" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "901353f9", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "train = interactions[[Columns.User, Columns.Item, Columns.Weight, Columns.Datetime]][\n", + " interactions[Columns.Datetime] < max_date - pd.Timedelta(5, \"D\")]\n", + "\n", + "test = interactions[[Columns.User, Columns.Item, Columns.Weight, Columns.Datetime]][\n", + " interactions[Columns.Datetime] >= max_date - pd.Timedelta(5, \"D\")]\n", + "\n", + "dataset_train = Dataset.construct(train)" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "id": "f08e3579", + "metadata": {}, + "outputs": [], + "source": [ + "popilarity_models = {\n", + " \"popular\": PopularModel(),\n", + " \"popular_mw\": PopularModel(popularity=\"mean_weight\")\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "id": "03c3bfb6", + "metadata": {}, + "outputs": [], + "source": [ + "popilarity_models[\"popular\"].fit(dataset_train)\n", + "popilarity_models[\"popular_mw\"].fit(dataset_train);" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "id": "0d7de49e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 24, 20, 31, 15, 167, 81, 89, 135, 355, 116])" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "popilarity_models[\"popular\"].popularity_list[0][:10]" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "id": "05ff208d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([11363, 11681, 12841, 13017, 2069, 13691, 13552, 13397, 11774,\n", + " 12913])" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "popilarity_models[\"popular_mw\"].popularity_list[0][:10]" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "id": "00ef735c", + "metadata": {}, + "outputs": [], + "source": [ + "pecos_pop = popilarity_models[\"popular\"].recommend(\n", + " users=test[Columns.User].unique(),\n", + " dataset=dataset,\n", + " k=100,\n", + " filter_viewed=False,\n", + ")\n", + "\n", + "pecos_pop_mw = popilarity_models[\"popular_mw\"].recommend(\n", + " users=test[Columns.User].unique(),\n", + " dataset=dataset,\n", + " k=100,\n", + " filter_viewed=False,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "id": "b302db55", + "metadata": {}, + "outputs": [], + "source": [ + "metrics = {\n", + " \"prec@5\": Precision(k=5),\n", + " \"recall@5\": Recall(k=5),\n", + " \"MAP@5\": MAP(k=5),\n", + " \"prec@10\": Precision(k=10),\n", + " \"recall@10\": Recall(k=10),\n", + " \"MAP@20\": MAP(k=20),\n", + " \"prec@20\": Precision(k=20),\n", + " \"recall@20\": Recall(k=20),\n", + " \"MAP@100\": MAP(k=100),\n", + " \"prec@100\": Precision(k=100),\n", + " \"recall@100\": Recall(k=100),\n", + " \"MAP@100\": MAP(k=100),\n", + " \"novelty\": MeanInvUserFreq(k=10),\n", + " \"serendipity\": Serendipity(k=10),\n", + "}\n", + "catalog = train[Columns.Item].unique()\n", + "metric_values_pop = calc_metrics(metrics, pecos_pop, test, train, catalog)\n", + "metric_values_pop_mean_weight = calc_metrics(metrics, pecos_pop_mw, test, train, catalog)" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "id": "9631093b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'prec@5': 0.0017855613317256697,\n", + " 'recall@5': 0.004623809755660008,\n", + " 'prec@10': 0.0011648975773029461,\n", + " 'recall@10': 0.005682095875283048,\n", + " 'prec@20': 0.0010502526799891945,\n", + " 'recall@20': 0.00880186008464912,\n", + " 'prec@100': 0.003247020220987923,\n", + " 'recall@100': 0.16609031082955295,\n", + " 'MAP@5': 0.0013179725619140792,\n", + " 'MAP@20': 0.0016695313583723814,\n", + " 'MAP@100': 0.005578924867474493,\n", + " 'novelty': 9.976033936531364,\n", + " 'serendipity': 1.2752762676592953e-05}" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "metric_values_pop" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "id": "5d55b781", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'prec@5': 9.09252633867684e-05,\n", + " 'recall@5': 0.00014799438063171262,\n", + " 'prec@10': 4.612151041357817e-05,\n", + " 'recall@10': 0.00015458316783365238,\n", + " 'prec@20': 2.635514880775895e-05,\n", + " 'recall@20': 0.00016946607539568094,\n", + " 'prec@100': 0.00015147621777259455,\n", + " 'recall@100': 0.0065476971391510656,\n", + " 'MAP@5': 3.0257754846536496e-05,\n", + " 'MAP@20': 3.1771198360212185e-05,\n", + " 'MAP@100': 0.00011355765992119742,\n", + " 'novelty': 17.423655787689828,\n", + " 'serendipity': 1.8991632826477633e-06}" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "metric_values_pop_mean_weight" + ] + }, + { + "cell_type": "markdown", + "id": "e5a4a011", + "metadata": {}, + "source": [ + "**На офлайн метриках выигрывает обычная модель по популярному**" + ] + }, + { + "cell_type": "markdown", + "id": "5875fab7", + "metadata": {}, + "source": [ + "# Save item_idf data" + ] + }, + { + "cell_type": "markdown", + "id": "6589996f", + "metadata": {}, + "source": [ + "Создаем датасет со взвешенными item-ами по механизму idf для использования в будущем" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "d62cabb9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
indexidf
095067.150811
116598.524953
271075.821207
376388.407093
466867.778734
.........
15701783314.822785
15702912514.822785
157031006414.822785
157041301914.822785
157051054214.822785
\n", + "

15706 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " index idf\n", + "0 9506 7.150811\n", + "1 1659 8.524953\n", + "2 7107 5.821207\n", + "3 7638 8.407093\n", + "4 6686 7.778734\n", + "... ... ...\n", + "15701 7833 14.822785\n", + "15702 9125 14.822785\n", + "15703 10064 14.822785\n", + "15704 13019 14.822785\n", + "15705 10542 14.822785\n", + "\n", + "[15706 rows x 2 columns]" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "item_cnt = Counter(interactions['item_id'].values)\n", + "item_idf = pd.DataFrame.from_dict(item_cnt, orient='index', columns=['doc_freq']).reset_index()\n", + "n = interactions.shape[0]\n", + "item_idf['idf'] = item_idf['doc_freq'].apply(lambda x: np.log((1 + n) / (1 + x) + 1))\n", + "del item_idf['doc_freq']\n", + "item_idf" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "7da47dfc", + "metadata": {}, + "outputs": [], + "source": [ + "item_idf = item_idf.sort_values(\"idf\", ascending=False)\n", + "item_idf.to_csv('../data/kion_train/items_idf.csv', index=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdce2b60", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/hw_2.ipynb b/hw_2.ipynb new file mode 100644 index 00000000..b4d81fa6 --- /dev/null +++ b/hw_2.ipynb @@ -0,0 +1,440 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "from pprint import pprint\n", + "\n", + "import copy\n", + "\n", + "from tqdm.auto import tqdm\n", + "\n", + "from implicit.nearest_neighbours import TFIDFRecommender, BM25Recommender\n", + "from implicit.als import AlternatingLeastSquares\n", + "\n", + "\n", + "from rectools import Columns\n", + "from rectools.dataset import Interactions, Dataset\n", + "from rectools.metrics import Precision, Recall, MeanInvUserFreq, Serendipity, calc_metrics, MAP, MRR\n", + "from rectools.models import ImplicitItemKNNWrapperModel, RandomModel, PopularModel\n", + "from rectools.model_selection import TimeRangeSplitter" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv('data_original/interactions.csv', parse_dates=['last_watch_dt'])\n", + "\n", + "df.rename(\n", + " columns={\n", + " 'last_watch_dt': Columns.Datetime,\n", + " 'total_dur': Columns.Weight\n", + " }, \n", + " inplace=True) \n", + "\n", + "interactions = Interactions(df)\n", + "\n", + "\n", + "users = pd.read_csv('data_original/users.csv')\n", + "items = pd.read_csv('data_original/items.csv')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_idageincomesexkids_flg
373089666262age_65_infincome_20_40Ж0
\n", + "
" + ], + "text/plain": [ + " user_id age income sex kids_flg\n", + "373089 666262 age_65_inf income_20_40 Ж 0" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users[users['user_id'] == 666262]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Функция для расчета метрик" + ] + }, + { + "attachments": { + "image.png": { + "image/png": "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AQQQQAABBBCInQCBBLGzpWcEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQSSnACBBEnuknHCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIxE6AQILY2dIzAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACSU6AQIIkd8k4YQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBGInQCBB7GzpGQEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgSQnQCBBkrtknDACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAKxEyCQIHa29IwAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggECSEyCQIMldMk4YAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACB2AkQSBA7W3pGAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAgyQkQSJDkLhknjAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQOwECGD6lGMAAEAASURBVCSInS09I4AAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkOQECCRIcpeME0YAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQCB2AgQSxM6WnhFAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEhyAgQSJLlLxgkjkDCBtGnTSokSJeT2YsXkg9Gj5c8//0xYh+yNAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAALJSuCiDSS47PLLJVPGjNbF/Ouvv2T//v1J6sLmyZNHKlWubJ3z4cOHZeKECUnq/DnZ2AqkTJlSChUuLKVLl5Frcl8jmTJlksyZs0jOnDkl59U55X//+591Ag/Uqydr166N7cnQOwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIJCmBiy6QIEWKFNKqdWvp3KWLpEmTxrpYv/z8s1SpVClJXbhGjRtLr969rXP+559/5OabbkpS58/JxkZA7+9OnTtLo0aNJWu2rHEehECCOIlogAACCCCA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T80/ZgQhgTczBXVAIPoSwDsy+t479BwEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAE/qsEICT4r955Me7vZs6iBAkS0L8PH9KRI0do8+ZNtGTx4v8wEQwdcyJy5kDZcuVkiBG++u5du2junDmR0xFcFQRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQEAQgJMA1AAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQ0AQgJNApkQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEICTAHAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABENAEICTQKJABARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAkABzAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAQBOAkECjQAYEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQABCAswBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABTQBCAo0CGRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQgJMAdAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQ0AQgJNApkQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEICTAHAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABENAEICTQKJABARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAkABzAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAQBOAkECjQAYEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQABCAswBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABTQBCAo0CGRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQgJMAdAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQ0AQgJNApkQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEICSIJnMgfvz4lDRpUtnb0NBQOnPmTMA9j4g2VadCQkLo7SJFKH369JQyVSpKkiQp3bh+nS5evECHDh6i9evX0Z07d1R1bEEABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABKIIAQgJosiNcNeN/AUKUL/+Ayj5S8l1tZLFitPZs/6LCSKiTe5c7Nix6YP69eXfSy+/rPtrZ27fuk2TJ02k4cOG0cOHD+3D2AcBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEIgkAhASRBJ4by4bM2ZMatvuU2ry4YcUI8YzDqeUKlmSTp065VDmzU5EtKmumyhxYho9ZizlzpNbFXncbli/ntq3a0fXhbcCJBAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAgcgnACFB5N8Dpz1IkyYNDRw8hLJlz+b0uD9CgohoU3UuYcKENGfefEqfIb0qkttrV6/R7t276MqVK5Q6dRrKkiULxY0X16HOqZMn6aNmzejE8eMO5dgBARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARB48gQgJHjyzD1esWq1avRlt+4UL348l3V9FRJERJtm53p+9RXVrlNHFz24/4CGDR1K302fRjdv3tTlMWLEoMpVqlD3Hj0pTtw4uvzg/gNUtUplhDnQRJABARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAgcghACFB5HB3edXcefLQzNmzHY5v3rSJlixaTP0HDdTlvggJIqJN3RGRyZkzJ82aO4+eeRR94d69e9Tqk09o/bp1ZjWH/GuvvUZjxo2nlKlS6vJOn31GixYu1PvIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIPHkCEBI8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IJrcz/jx41PSpEllb0NDQ+nMmTMB9TwkJIQKv/025cyVi1566SWKFzceXbp8iU6dPEmrVq2iSxcvBtS+s5P5mm8XKULp06enlKlSUZIkSenG9et08eIFOnTwEK1fv47u3Lnj7FSUgQAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIPCECEBI8IdCBXCZ/gQLUr/8ASv5Sct1MyWLF6exZ/8QELB4YPmIkJU0WJkzQjT7KPAx9SGPHjqEhgwbZh/zajx07Nn1Qv778e+nll122cfvWbZo8aSINHzaMHj586LIeDoAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACEQcAQgJIo5twC3HjBmT2rb7lJp8+CHFiPGMQ3ulSpakU6dOOZR5s8MeAUaMHEVx4sbxWH3h/AX0RdcuxB4Q/E2JEiem0WPGUu48ub1uYsP69dS+XTu6LrwVIIEACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACDxZAhASPFneXl8tTZo0NHDwEMqWPZvTc/wREhQvXoJGjBpFMZ+Nqdv84/Bh2rVzF927d5dyvJlTXC+7g2hh4YIF1Pnzz3V9XzIJEyakOfPmU/oM6R1Ou3b1Gu3evYuuXLlCqVOnoSxZslDceHEd6nCIhY+aNaMTx487lGMHBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAgYglASBCxfP1qvWq1avRlt+4UL348l+f7KiR45plnaMWq1ZTh1Qy6zX7ffksTJ0zQ+5ypVLkyfduvvxYTPHz4L71booRfYRR6fvUV1a5TR7f/4P4DGjZ0KH03fRrdvHlTl8eIEYMqV6lC3Xv0dPCUcHD/AapapTLCHGhSyIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIBAxBOAkCDiGft0hdx58tDM2bMdztm8aRMtWbSY+g8aqMt9FRJkyZqVFi5erM+fOH489evbV++bmU9ataLWbdrooi6dOtOC+fP0vjeZnDlz0qy580joF2S6d+8etfrkE1q/bp3L01977TUaM248pUyVUtfp9NlntGjhQr2PDAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiA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AAIgAAIgENkJQEgQ2e8g+g8CTABCAjwG3hJInjw5vVe0qKp+lOOB/7Rypbenot4TQgAGySfkRqAbIOADAby3PsBCVRAAARAAARAAARAAARAAARAAARAAARAAARAAgSeCAIQET8RtQCdAIDACJd9/n/IXKKAa+X3bNvphxozAGsTZIAACTyyBkJCY1OmLzhQ1alSi+0T9+n5DFy9efGL7i46BAAgQ4b3FUwACIAACIAACIAACIAACIAACIAACIAACIAACIBDZCEBIENnuGPoLAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAhFIAEKCCISLpkEABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAgshGAkCCy3TH0FwRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAQikACEBBEIF02DAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQGQjACFBZLtj6C8IgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIRCABCAkiEC6aBgEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAIHIRgBCgsh2x9BfEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEIhAAhASRCBcNA0CIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACkY0AhASR7Y6hvyAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQgQQgJIhAuGgaBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABCIbAQgJItsdQ39BAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAIAIJQEgQgXDRNAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAhENgIQEkS2O4b+ggAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgEAEEoCQIALhomkQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQiGwEICSIbHcM/QUBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEACBCCQAIUEEwkXTIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIBDZCEBIENnuGPoLAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAhFIAEKCCISLpkEABEAABJ5NAu/mz0/f9O2rBr961Wrq2KG9C4iChQpR9x49KHr06LR27Vpq/9lnLscj+07MmDHplVfiqWFcvnyJrl696jKkNGnSUK7cuenndevor7/+cjmGHRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAgcdPAEKCx38PvOpBrFixKEGCBKru3bt36fjx416d56mSGHrSp3+b3s7wNsXk9vft3Uu7d+2iU6dOeTrNq2PPPfccvfbaa6runTt36cQJ7/sbJUoUSp48uXUdGauMGQkEQAAEIguB3Hny0PiJE1V37/x3h/LkzkWXL12yuj946FAqUbKk2l8wbz61a9vGOuZvRubdTJkykVw7UaL/UdxX4tK/t/+l8xfO06GDB+mnlSvpktEHf6/jzXk9vvqKqlarpqqu+uknavzxx9ZpRd57j74bMULt379PVOfDWvTbr79ax5EBARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARB4/AQgJHj89yDcHsiqzW/69qNE/0tk1S1SsJBPxnnrRM7kzJWLvujShVKlTkNRojxnHlL5f/75h2bO+IEGDuhP9+7dC3PcmwK5xsTJk62q77Jh69y5c9a+p0z9Bg3o8w4drCpNmzShFcuXW/vIgAAIgMCTTiBGjBi0eds25XFA+jpx/Hjq+fXXdJ8t55kyZVbzY4yQGGoYHdu3p9mzZgU0pPTp09MgFieYIix7g/fu3qNx48ZSf/aUENHirK979qJKVSqrLqxZvYY+bviR1Z0hQ7+l4iVLWPszf/iBOnfsaO0jAwIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIg8PgJQEjw+O+B2x5EixaNWrZqTQ0aNgxj8C9WpAgdPXrU7bnuDlSvUVOJCKJGi+quilW+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AAIgAAIgAAIg4CUBCAm8BIVqIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIPAsEICQ4Fm4yxgjCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACHhJAEICL0GhGgiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAg8CwQgJHgW7jLGCAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAJeEoCQwEtQqAYCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACzwIBCAmehbuMMYIACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACICAlwQgJPASFKqBAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAwLNAAEKCZ+EuY4wgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIg4CUBCAm8BIVqIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIPAsEICQ4Fm4yxgjCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACHhJAEICL0GhGgiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAg8CwQgJHgW7jLGCAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAJeEoCQwEtQqAYCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACzwIBCAmehbuMMYIACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACICAlwQgJPASFKqBAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAwLNAAEKCZ+EuY4wgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIg4CUBCAm8BIVqIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAAC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CdzlidxAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAIEoTgJAgSl8+FB4EQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAE3CUAIYG7PJEbCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACERpAhASROnLh8KDAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAgLsEICRwlydyAwEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAIEoTQBCgih9+VB4EAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEHCXAIQE7vJEbiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQpQlASBClLx8KDwIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAALuEoCQwF2eyA0EQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEojQBCAmi9OVD4UEABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEDAXQIQErjLE7mBAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQJQmACFBlL58KDwIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIuEsAQgJ3eSI3EAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEIjSBCAkiNKXD4UHARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAXcJQEjgLk/kBgIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAJRmgCEBFH68qHwIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIOAuAQgJ3OWJ3EAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAgShOAkCBKXz4UHgRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAATcJQAhgbs8kRsIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIRGkCEBJE6cuHwoMACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACICAuwQgJHCXJ3IDARCIIgRix45NuXPnphw5c9LkSZPozz//jCIlRzFBAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAIGIJRBshQcyYMalAwYL0Zo4clDx5cooTJw6dP3+eDh86RIsWLaJrV69GLOkAcn/iiScoTZo09FzSpBQ3blw6eeKE7dF/XrlCV8UfAgiAgD2BGDFi0KuvvUb58uWnVKlT0dNPP03PPJOQUqRIQSleTEFPPfWUPPDdatVox44d9pkgFgRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAASiGYFoISR49dVXaeSYMVJAYHd9//nnH+rWpSvNmvmD3e5HHvdWnjw0eepUR+e9eeMmrV69iqZMnkxbt2xxdAwSgcDjToDFOK0//phq1apNiRIn8ltdCAn8IkICEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEACBaETgsRcSZH/zTRr37bf0dIIEfi9r/379aNzYsX7TRXSCfPnz0wRhaj2QcO/uPWrX9hNauGBBIIchLQg8dgSefPJJ6iue5XLlyzuq29YtW6lNq5bSQomjA5AIBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABB5zAo+1kCB79uxiQH4yxY0X17qMvx08KGbub6V79+9Rjhw5KVPmTNY+XunetSt959AagMeBLm7YCQluXL9hnSFmrJiWSXYrUqywmKB9u7a0YP58PRrrIBBtCLALk3HjJ1C+/Pk86nz+3HnasmUzHTxwgK5fv04XLlykU6dO0gnhNiQyuTXxKDQ2QAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQOA/IvBYCwlYRKAPKA4bMpSGDxvqgbqNMH/e/KOPrLhzZ89R0cKF6O7du1bco14xhQSlihen48ePexTjuaRJKUOGDPThRy0oV+5c1j4W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/dPcuZQ5y6v0RJgRAusko0aMoMFff21tYwUEQMCeAIQE9lwQCwIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAJRhQCEBFHlSqGcIGBDoGKlSpQkSRK5Z85PP9Hly5dtUiEqUAKr1qylFC+m8DhsxvTp1L1rV7p7965HPDZAAATCE0iUKBFVqVpV7jh37hwtXLAgfCIfMVmzZaOcOXPKFFu2bKFdO3f6SP1odqG9fTSccRYQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAIHIQQBCgshxHVAKEACBSERg6rTplCBBArp/7x4dOnSI1q9fR3PnzIlEJURRQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQCDiCEBIEHFskTMIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIRDkCEBJEuUuGAoMACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIBAxBGAkCDi2CJnEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEIhyBCAkiHKXDAUGARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAgYgjACFBxLFFziAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQ5QhASBDlLhkKDAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIRRwBCgohji5xBAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAIMoRgJAgyl0yFBgEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEIo4AhAQRxxY5gwAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgECUIwAhQZS7ZCgwCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACEQcAQgJIo4tcgYBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEACBKEcAQoIod8lQYBAAARAAARAAARAAARAA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18amT3YSi3Znn3OOOeXUU80BBxxgdimxi/lm0zfm888+MzNnzjTfbNyYLIuMzp940knmhBNONAcffLA58KADbR5fffmVWb9+vVm1cqV59913MsoXF+21116mUuXK5pRTTjF77rmX2WXXXcz++5ey99pn331svvNff920bd0643vohYqAIpAbBNq0bWtu6d7dZj5zxgxzQ8eOubnRPzxXbSf/4RVAH18RcBDoSos3V7Zvb48uWbzEXN6sqZNCf/5TEMDc4A2qA3uU3MP89edfpl7dOuaD1av/KY+vz6kIFCkEsMB+0EF5BNvff/89JWIPxnglS5YMnmPTpk3m559/Dn4X5g7m5KPHjrW33PK/LaZypTMTiGr3Dx1qLqpZ056f9uxzpmuXzoVZPL2XIqAIKAKKgCKgCCgCioAioAgoAopAGggokSANsLZV0jMrVSJG/yBT6oBSQRGqV6lq1q3LjEwA8sDQBx8y++2fR0wIMs3fgfJwxIjh5r4hQ9xTGf+uW6+euaJlS3PCiSfG5gEywdgxY8yzzzxj/v7779i0fPLoo482/QbcbfPeaad/8eHQrRIJQmHRg4rANkVgXyJJzZ4zx+y222703RtzWYP6ZsXy5du0TDvazbWd3NHeqD6PIuAHARC4QOSCvLVsmWnauLGfjDWX7RKBa6+7znS68UZbdiWWbJevUAu9gyDQtFlz06tPb/s0//vf/8wJ5crFPtmRpUub8RMmWGI9J7xnwADz6COP8M9C3RYvXtwseest63EANx47erTp17evnd+XL3+yGfvkk6Z4ieK2TLd262amTJ5cqOXTmykCioAioAgoAoqAIqAIKAKKgCKgCKSOgBIJUseq0FMWK1bM3HDjTaZtu3bGXSC/sHp18/nnn6ddJrgZfPChh4OJe1wGUydPMbf1uNXAA0Km8p///IcW+QeYWrVrp5UFLJJv6drV/PHHH7HXVahY0QwbPsJaT8UmpJM//fijuf+++8wT+dYRydLreUVAESgcBPrffbep36CBvdkzU6eqe1PPsGs76RlQzU4R2IEQUCLBDvQyPTwKxu0zZ79oDj7kYJvbjZ06aUgwD7hqFopAughc3qKFuf2OO+xlIPmXO/aYyCz2239/M2HipOC7RcJZ5GEQ3+9ff/0VeV2uT9za4zbTsnWr4DbwfPgDhc857vjjKYTKv+3xL0if0ahh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n7WTAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQFwRoCJBXN0ONoYESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEUpYAFQlSlj9rJwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIG4IkBFgri6HWwMCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACaQsASoSpCx/1k4CJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACcUWAigRxdTvYGBIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJIWQJUJEhZ/qydBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABOKKABUJ4up2sDEkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkLIEqEiQsvxZOwmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAnEFQEqEsTV7WBjSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESCBlCVCRIGX5s3YSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESiCsCVCSIq9vBxpAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBAyhKgIkHK8mftJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJBBXBKhIEFe3g40hARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggZQlQEWClOXP2kmABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEggrghQkSCubgcbQwIkQAIkQAIkQAIkoAlcdtllUqxYMbmnSBGZPm2a/Pnnn/oUtyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAjEkQEWCGML1sugMGTJI5syZnSJPnz4te/fujbr49OnTyx133Cl3FrhT0qvyt33/vWzdskV++eWXqMs2C8iaNaukS5fOjApr36vrDKsyJiIBEiABEiABEkgRAqlSpZI77rxTSpYsJTlz5ZQrrrhCrroqo2TLlk2yZc8ml19+udOuBvXqyddff50ibWSlJEACJEACJEACJEACJEACJEACJEACJEACJEACJHCxEaAiwQVwx4uXKCGDBg+RrNdndVtbsVx52bcvacoE9xYvLt179JA8eW+RVKkuccvUO1jtN+/NufLqsKFy5swZHZ3k7Ruz50iRokWSlD+a60xShcxEAiRAAiRAAiSQLAQuueQSad+xozRq1FgyXZ0pZJ1UJAiJiAlIgARIgARIgARIgARIgARIgARIgARIgARIgARIwDMCVCTwDKX3BaVJk0Y6dOwkLZ94IpHAv3LFirJ79+6IK23UuImjRJA6TeqQedeuWSOdOnSQI0eOhEwbLMHb77wj+fLnD5Yk4LmkXmfAAnmCBEiABEiABEggxQlceuml8sqgQVKjZs2w2vLlF19Kh3Zt5cCBA2GlZyISIAESIAESIAESIAESIAESIAESIAESIAESIAESIIHoCFCRIDp+Mct94403ytBXX5MCBQv4rSMpAvZmzVvICy+96FPe/l9/le+2fifHTxyXvHnzqj9lpSB1KjfNN8qEcH1lSjiasGbdOrlBmSdGOHPmrMhZ9RcgmHUjSVKuM0DRjCYBEiABEiABEogDAqlTp5aJk6dIyVIlfVpz4LcD8sUXnzuulo4ePSoHD/7uWF/as2ePHP7rL5+0PCABEiABEiABEiABEiABEiABEiABEiABEiABEiABEogtASoSxJZvkkqvU7eushrQU9JnSB8wf1IE7O99sNrxPYxC//nnH+nft5/MnvWGTx3FihWTUWPGylUZr3Ljmz76qHzy8cfucaQ7XyplBPg7RujaubMsWbw4YBElS5WSKdOmueeTcp1uZu6QAAmQAAmQAAnEHYG69R6W/q8McNv16SefyEsvvCB79ybNZZNbEHdIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAQ8I0BFAs9QelPQPUWKyKw5c3wKW//hh7J44SIZPGyoGx+pgL3w3XfLnLlz3fydlU/ipW+/7R6bO+XLV5CxE8a7UcOGDJVxY8e4x5HspEqVSrZu+0GUG2QntHriSVm9+oOARVCRICAaniABEiABEiCB/wSBle++KzfdfLNzLd98/Y00e+xROXbs2H/i2ngRJEACJEACJEACJEACJEACJEACJEACJEACJEACJPBfIUBFgji7k8XuvVdmvHHOSsC///4rQwcPlqlTpkiJkiWjWqn//AsvSvOWLZyr/fPPP6V0iRJy6tQpv1efPn162bhpk3tu5owZ8nLv3u5xJDtXXXWVfP7VV26Wxg0bypdffOEe2ztJUSQoUrSolClTVrJnzybXXnedHD78t/z6yy+yUdX73nvvBrxO+Gdu2KiRpE6dxm5G0ONTp/5VlhxmyenTpwOmS2qbzALhiiLcsGnTN/LVl1+6yc1rO6tcScx98005fty/kAYmphs0bCSXXXaZk3/Bgrd8TEjffc89UrBgIefctm3fy8cffeTWY+8ULFRI7r77Hif66NEjMi9BeeWOO++UokWL2clDHn/33VbBSlU7ZMqUSWo/+KDkUe44sim3GadOnZZffvlZtmzeLO8sXSonTpyws/gcFy5cWArdVdgnLtDB6dOnZM7s2YLnUYdImOg8euvlvdFl6q0X/U6XFWgbLTu73KS22eRol+nv2OzXkdy/QH0adaRJk0YaNW7svEPwnM2fN1dgjv3m3LmlcuUqjruY6zJnVubZD8jOHTtk0cKF8qtyJ2OH+g0aSPr0GZzorzd+JV8rKy7BQrXq1SVr1uudJF4/IzbXI0eOONcVrD3mubLlysnNN+d2okwmZppg+3b9dtozZ047DHf89JP8pP78Ba/ui7+yk/rusa8r1lyzZs0qdxYoIDly5FSKfAmafMYF2d8M41RYu1dlzCg1a9aUW27JJ9ffcINcemka+W3/b7Jnz27nHRzMsgAUCKBIgPD34cNSoXx555sDRnDrhPIOq/hf1Hf894MH/bbnlnz5pFSp0s65s2fPyIzp05XrpDN+0yIS34nKVaq65+fMnuV8Jx6qU0cyZszkxK9atVJ++flnN425ky9/filZspQT9fPP++TdVavM04n2i6sxVunS9zlWoK66KqNzHfv27ZUVK1Y4bhsSZVARZh+Jh++2vzYiLtZ9K1C9jCcBEiABEiABEiABEiABEiABEiABEiABEiABEkh+AlQkSH7mQWvUigS7d+2Sju3by5YtW5z0SRGwmxVBsFVACRUQ9itB1meffWae9tm/TgnjNxjCW7hAmDZ1ik+acA+yZc8u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eMCCeYIESIAEPCDgpcC6jnrXDRg40KdVBw8clDatW8mmb77xiedBcAJe3pfgNfFsShNYvXadZMuezacZc2bPlj69esnp06d94nlAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiQQGQEqEkTGi6lJgARIgARIwCGQLl16eal7N0mdOrXIWZEhgwfJH3/8kSQ691eqJC916y6HDv0pf/7vT/nhhx9k6pTJsn///iSVdzFn8vK+XMwcL4Rrnzlrtlx55ZVy9swZ+fHHH2X9+g9l8aJFF0LT2UYSIAESIAESIAESIAESIAESIAESIAESIAESIAESiHsCVCSI+1vEBpIACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBA8hGgIkHysWZNJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJBD3BKhIEPe3iA0kARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggeQjQEWC5GPNmkiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEgg7glQkSDubxEbSAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQALJR4CKBMnHmjWRAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQNwToCJB3N8iNpAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEko8AFQmSjzVrIgESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIG4J0BFgri/RWwgCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACSQfASoSJB9r1kQCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACcU+AigRxf4vYQBIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJIPgJUJEg+1qyJBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABOKeABUJ4v4WsYEkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkHwEqEiQfKxZEwmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAnEPQEqEsT9LWIDSYAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESCD5CFCRIPlYsyYSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESiHsCVCSI+1vEBpIACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBA8hGgIkHysWZNJEACJEACJEACBoFb8uWTs2fOyPbt243Yc7t33Hmn/H34sOzZsyfROUaQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAnElgAVCWLL17PSM2TIIJkzZ3bKO336tOzdu9ezsnVB2XPkkEvTpNGHcuTIEfn999/dYy92UqdOLfeVKSO5c+eWHDlzynXXZZYjf/8tBw78Jt9/972sWbNaTpw44UVVLIMESIAESCCOCfTp21caNGzotHDKpMnyyoD+bmvHjBsnFSpWlLNnRYYMHiQTx493z3GHBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEgg9gSoSBB7xlHXULxECRk0eIhkvT6rW1bFcuVl3z7vlAmK3XuvTJ/5hlxyiVuF7Nq5U6pUqnQ+Ioq9yy+/XB597DHn7/obbghY0vFjx2XK5EkycsQIOaNWqTKQAAmQAAn89whcdtllsvGbTZLm0nPKayeOn5BCBe50LjRLlizy4UcfuRe9b+8+qVi+nHvMHRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggdgToCJB7BknuYY0yjpAh46dpOUTT0iqVIaEX5VYWa3U3L17d5LLNjOmS5de3n7nHcmZK6cZ7ZiTrlShgk9cUg4yZsokY8aOk3uK3BN29rVr1kjnjh3lb2WtgIEESIAESOC/ReASpbW26r33JNeNNzoXBos0tWvWcPaheLZu/QbJdHUm53j9hx9Ky+bN/1sAeDUkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkEOcEqEgQpzfoRiVcGfrqa1KgYAG/LfRSkaBbjx6OpQC7IviljlaR4KqrrpI3582X3Hly+xR/+K/DsnHjV/LHH39Irlw3yh133CHp0qfzSbN71y556sknZeeOHT7xPCABEiABErjwCRQuXFgaNWniXMi0qVNly+bN7kWVLl1aaj/4kJw+c1rGK0W0HTt+cs9xhwRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIIPYEqEgQe8YR11Cnbl3p3qOnpM+QPmBerxQJCt99t8x+c66PSwNdqReKBL1fflkaNmqki5RT/56SEcOHy8wZ0+Xo0aNufKpUqeTBhx6Snr16S9p0ad3477ZslToPPUg3By4R7pAACZAACZAAh0lPLgAAQABJREFUCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBAbAlQkSC2fCMu/Z4iRWTWnDk++WDWefHCRTJ42FA33gtFAvioXrTkbcmTN49T7v+UdYBFixZJi5YtneNoFQmw2nT23HmuksI///wjbZ9+WtasXu1eh71zyy23yNjxEyRHzhzuqeeffVYWLljgHnOHBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEggdgSoSBA7tkkqudi998qMN95w8v77778ydPBgmTplipQoWVKmTJvmlumFIkH7Dh2kzTPPuGW2b9tOuRnIJZ27dnHiolUkGDJ0mNSsXcstf/CgQTJx/Hj3ONDO/ZUqyagxY9zTa1avkaeeeNw9tneKFC0qZcqUlezZs8m1110nhw//Lb/+8ots/Ooree+9d+XUqVN2Fsf6QaZMVzvxJ0+ekNmzZiVKoyOqVqsu119/vXN4+PBfsuCtt5z9NGnSSKPGjSV16jRy9uxZmT9vrmNl4ebcuaVy5SqSN29euS5zZjl48IDjnmHRwoXy66+/6mIDbuEfvGixYpIvXz7Jlz+/ZM16vezdu0e+27pVtioLDd99t1WglOEv3H3PPVKwYCHn1LZt38vHH33kL5kTd2eBAlKkSFFnf+fOHbJ2zZqAaaF0kv/WW+X222+XdOkSW8o4ffqUzJk9W9BnA4XiJUpI6dL3Sc5cOeWqqzLK7wcPyr59e2XFihWy7fvvA2UT85qQaP2H62T79u0B05snrsqYUerUqetG/fDDNvlowwb3ONTOpZde6ljUwD1GOHLkiHOfQ+XT58uWKyc333zOrYfZR/R5exsJI1jx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WjQ3qyD+gAgknPmFBLH0YZcV9jdq5Eh5ceDAcLuWKVOnSaEbC5lyCBgmTjgRTcBtK4i7bbRO3XrSu28fuygI41+0cGETAcHtN4ioUVnZ792zx6vrzpQqXdpETsE6tHHrTTe5xUnOu/sKqoz9r1u3Vv+tk1madmatRjeJxfLkySPNWzwgNe6pGZIKBm3iXvPhh/NNdIYvv/giyd1E01+SbMypsPrrNZIxU0azpnmTprJ06fF0H04VvUfGdm9w20qt83D3wXkSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESOJUIUEiQzn5tjPLEaGw3hP/JdopX6Ej3vHnzmRzncCRlzJjRpDjYvOk7TWPwlYb9Vo9rlHbeeedJ7kuO50xHOoWfdVQ7coK7I9OjbDLJ6kFO2G/WrDHbZciQQc8xr5ym5wfH3tatPwpECMk1OKBz5brQOOKQluGXX35J7qYndb0LNJXBxblzm98YTnikbjhw4MBJfcxpfXBpySg1+3BaczsV94doL7n1eoLwAvf8o/pvh0ZegZjsv2Zp2e//a2x4vCRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTwbxCgkODfoM59pgsCkZyw6eIEeRLpngD7cLr/iXmCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJJAiAhQSpAgbNyIBCUxtYCMSkA8J/BcIUEjwX/iVeIwkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkPYEKCRIe+bcYzohQCdsOvkhT+HTYB8+hX98njoJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJRCBAIUEEOCwigUgE6ISNRIdl/wUC7MP/hV+Jx0gCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACaU+AQoK0Z849phMCGTNmkq7dnpYzzjhD5B+RAf1fkH379qWTs+NpnAoE2IdPhV+Z50gCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEAC0ROgkCB6ZtyCBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABNItAQoJ0u1PyxMjARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggegJUEgQPTNuQQIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQALplgCFBOn2p+WJkQAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkED0BCgkiJ4ZtyABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBdEuAQoJ0+9PyxEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEggegIUEkTPjFuQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQLolQCFBuv1peWIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkED0BCgmiZ8YtSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESCDdEqCQIN3+tDwxEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEoieAIUE0TPjFiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQbglQSJBuf1qeGAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAlET4BCguiZcQsSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESSLcEKCRItz8tT4wESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEoidAIUH0zLgFCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACaRbAhQSpNuflidGAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAtEToJAgembcggRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgATSLQEKCdLtT8sTIwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIHoCVBIED0zbkECJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEAC6ZYAhQTp9qfliZEACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBA9AQoJIieGbcgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggXRLgEKCdPvT8sRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIIHoCFBJEz4xbkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEC6JUAhQbr9aXliJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJBA9AQoJomfGLUiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEgg3RKgkCDd/rQ8MRL47xC49NI8UrpMafn1119l9qxZ/50D55GSAAmcFAQyZswoF1yQwxzL73/8Lvt/++2kOC4eBAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAn8VwmcUkKCDBkySP4CBaRQoUKSJUtW+fbbjfLNmjXy008/nVS/32mnnSaXX3655MiZU+Ac2bZ1a+Dx/aqOEjpLAtFw5UlI4LzzzpNid9whN998s2TPfp5kypxJcuW6UC655BK5IMcF5oiXfPKJtGjW7CQ8eh4SCZDAyUygY6dO8kDLluYQV3y+Qho3bHAyH27gsZ199tmSO3fuwDK78sCBA0Zw9c8//9hV6XaaK1cuyZQpU9Tnt337djl27FjU23EDEiABEiCBUAJ4d8+WLZtZ+fPPP8vRo0dDK3CJBEiABEiABEiABEiABEiABEiABEgg3RM4JYQEV199tfTu208KFiwoZ5x5RqIf9ZdffpHXJk2WEcOHycnwcf72okXl1cmTEx1n0IrDhw7LokULZdKrr8oXK1cGVeE6EvhXCeD669PveSl4ww1y+umnRTwWCgki4mEhCZBAGAJPdu4szVu0MKVffvGFNLj33jA1T97VDRo2kp7P9EryAP/880/ZvWu3rFjxuUyd8o6sXLnipHh3SfLAo6yw/LPP5PwLjovMotm0/eOPy8wZM6LZhHVJgARIgAQCCEx+/Q0pXKSwKenbu49MnDA+oBZXkQAJkAAJkAAJkAAJkAAJkAAJkAAJpGcC6V5IUKJkSRky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QUAgwTiUYgZ5OHzkSDr11tvp9u35NP/3fJqZmUk/fP9dun79egapl8RxEHj26NH09MGDZVZ+u3IlnZuaGodsyUPHBbZPTKTXT54sFRb+XUjvvfuOTqKO1wnZJ5CzQFvai9oMOdciaR+VwIsnTqQn9u4td3fh/Pk0ffnyqHZtPwQIECBAgAABAgQIECBAgEAmAgIJMikoySRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAk0ICCRoQtkxCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAJgICCTIpKMkkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJNCAgkaELZMQgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQCYCAgkyKSjJJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECTQgIJGhC2TEIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEAmAgIJMikoySRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAk0ICCRoQtkxCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAJgICCTIpKMkkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJNCAgkaELZMQgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQCYCAgkyKSjJJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECTQgIJGhC2TEIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEAmAgIJMikoySRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAk0ICCRoQtkxCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAJgICCTIpKMkkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJNCAgkaELZMQgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQCYCAgkyKSjJJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECTQgIJGhC2TEIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEAmAgIJMikoySRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAk0ICCRoQtkxCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAJgICCTIpKMkkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQJNCAgkaELZMQgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQCYCAgkyKSjJJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECTQgIJGhC2TEIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEAmAgIJMikoySRAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAk0I/AfN+6wQWxy+cwAAAABJRU5ErkJggg==" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![image.png](attachment:image.png)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [], + "source": [ + "# Модели: rectools.models.RandomModel(random_state=32), rectools.models.PopularModel() с параметрами по умолчанию\n", + "models = {\n", + " \"random\": RandomModel(random_state=32),\n", + " \"popular\": PopularModel()\n", + "}\n", + "\n", + "# Метрики: 2 ранжирующие, 2 классификационные, 2 beyond-accuracy. Считаем по порогам 1, 5, 10. MAP обязательно\n", + "metrics = {\n", + " # классификационные\n", + " \"prec@1\": Precision(k=1),\n", + " \"prec@10\": Precision(k=5),\n", + " \"prec@10\": Precision(k=10),\n", + " \"recall\": Recall(k=1),\n", + " \"recall\": Recall(k=5),\n", + " \"recall\": Recall(k=10),\n", + " # ранжирующие\n", + " \"MAP\": MAP(k=1),\n", + " \"MAP\": MAP(k=5),\n", + " \"MAP\": MAP(k=10),\n", + " # среднее значение обратного ранга\n", + " \"MRR\": MRR(k=1),\n", + " \"MRR\": MRR(k=5),\n", + " \"MRR\": MRR(k=10),\n", + " \"novelty\": MeanInvUserFreq(k=10),\n", + " \"serendipity\": Serendipity(k=10),\n", + "}\n", + "\n", + "# 3 фолда для кросс-валидации по неделе\n", + "n_splits = 3\n", + "test_size = \"14D\"\n", + "\n", + "# Инициализированный Splitter для кросс-валидации\n", + "cv = TimeRangeSplitter(\n", + " test_size= test_size,\n", + " n_splits=n_splits,\n", + " filter_already_seen=True,\n", + " filter_cold_items=True,\n", + " filter_cold_users=True,\n", + ")\n", + "\n", + "# Количество рекомендаций для генерации (K)\n", + "K_RECOS = 10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = Dataset.construct(\n", + " interactions_df=interactions,\n", + " user_features_df=None,\n", + " item_features_df=None,\n", + " )\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 5 µs, sys: 0 ns, total: 5 µs\n", + "Wall time: 32.2 µs\n" + ] + } + ], + "source": [ + "%%time\n", + "import time\n", + "\n", + "def evaluate_models(interactions, models, metrics, cv, K_RECOS):\n", + " results = []\n", + " trained_models = {}\n", + "\n", + " # n_splits = cv.get_n_splits()\n", + " fold_iterator = cv.split(interactions, collect_fold_stats=True)\n", + "\n", + " for train_ids, test_ids, fold_info in tqdm(fold_iterator, total=n_splits):\n", + " print(f\"\\n==================== Fold {fold_info['i_split']}\")\n", + " pprint(fold_info)\n", + "\n", + " df_train = interactions.df.iloc[train_ids]\n", + " # Создаем RecTools Dataset через метод construct на train взаимодействиях для каждого фолда\n", + " dataset = Dataset.construct(df_train)\n", + " # Определили test\n", + " df_test = interactions.df.iloc[test_ids] # Предполагается, что Columns.UserItem определено\n", + " test_users = np.unique(df_test[Columns.User])\n", + "\n", + " catalog = df_train[Columns.Item].unique() # Каталог для рекомендаций\n", + "\n", + " # Обучаем модель (не забываем сделать deepcopy), рекоменуем K айтемов для каждого юзера, считаем метрики на test\n", + " for model_name, model in models.items():\n", + " \n", + " model_copy = copy.deepcopy(model)\n", + " # время перед началом обучения\n", + " start_time = time.time()\n", + " model.fit(dataset)\n", + " recos = model.recommend(\n", + " users=test_users,\n", + " dataset=dataset,\n", + " k=K_RECOS,\n", + " filter_viewed=True,\n", + " )\n", + " metric_values = calc_metrics(\n", + " metrics,\n", + " reco=recos,\n", + " interactions=df_test,\n", + " prev_interactions=df_train,\n", + " catalog=catalog,\n", + " )\n", + " \n", + " # время обучения\n", + " training_time = time.time() - start_time\n", + "\n", + " res = {\"fold\": fold_info[\"i_split\"], \"model\": model_name, \"training_time\": training_time}\n", + " res.update(metric_values)\n", + " results.append(res)\n", + "\n", + " # Сохраняем обученную модель\n", + " if fold_info['i_split'] == n_splits - 1: # Последний фолд\n", + " trained_models[model_name] = model_copy\n", + " \n", + "\n", + " # Результат оборачиваем в pandas DataFrame и усредняем по фолдам\n", + " results_df = pd.DataFrame(results)\n", + " average_results = results_df.groupby('model').mean()\n", + " average_results = average_results.reset_index()\n", + " return average_results, trained_models\n", + "\n", + "# %%time\n", + "# df_rec, trained_models = evaluate_models(interactions, models, metrics, cv, K_RECOS)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/3 [00:00\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pct
017654995062021-05-11425072
169931716592021-05-298317100
265668371072021-05-09100
386461376382021-07-0514483100
496486895062021-04-306725100
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + " \n", + " " + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct\n", + "0 176549 9506 2021-05-11 4250 72\n", + "1 699317 1659 2021-05-29 8317 100\n", + "2 656683 7107 2021-05-09 10 0\n", + "3 864613 7638 2021-07-05 14483 100\n", + "4 964868 9506 2021-04-30 6725 100" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b4omWvMOjZzX", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:41:21.721270Z", + "iopub.status.busy": "2023-01-22T12:41:21.720745Z", + "iopub.status.idle": "2023-01-22T12:41:22.116852Z", + "shell.execute_reply": "2023-01-22T12:41:22.115397Z", + "shell.execute_reply.started": "2023-01-22T12:41:21.721229Z" + }, + "id": "b4omWvMOjZzX" + }, + "outputs": [], + "source": [ + "interactions_df = interactions_df[interactions_df['last_watch_dt'] < '2021-04-01']" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "JAuH-fG0jZzX", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "execution": { + "iopub.execute_input": "2023-01-22T12:41:23.195240Z", + "iopub.status.busy": "2023-01-22T12:41:23.194661Z", + "iopub.status.idle": "2023-01-22T12:41:23.202760Z", + "shell.execute_reply": "2023-01-22T12:41:23.201745Z", + "shell.execute_reply.started": "2023-01-22T12:41:23.195188Z" + }, + "id": "JAuH-fG0jZzX", + "outputId": "3e259108-40e9-48fc-c212-1bbd7e9e21a1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(263874, 5)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "rWCoSNwWjZzX", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "execution": { + "iopub.execute_input": "2023-01-22T12:41:25.368988Z", + "iopub.status.busy": "2023-01-22T12:41:25.367925Z", + "iopub.status.idle": "2023-01-22T12:41:25.558751Z", + "shell.execute_reply": "2023-01-22T12:41:25.557372Z", + "shell.execute_reply.started": "2023-01-22T12:41:25.368937Z" + }, + "id": "rWCoSNwWjZzX", + "outputId": "c7738105-161d-4c43-9028-21b64809ad04" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# users: 86614\n", + "# users with at least 5 interactions: 14563\n" + ] + } + ], + "source": [ + "users_interactions_count_df = interactions_df.groupby(['user_id', 'item_id']).size().groupby('user_id').size()\n", + "print('# users: %d' % len(users_interactions_count_df))\n", + "users_with_enough_interactions_df = users_interactions_count_df[users_interactions_count_df >= 5].reset_index()[['user_id']]\n", + "print('# users with at least 5 interactions: %d' % len(users_with_enough_interactions_df))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "qDCcr1_UjZzY", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "execution": { + "iopub.execute_input": "2023-01-22T12:41:27.227318Z", + "iopub.status.busy": "2023-01-22T12:41:27.226717Z", + "iopub.status.idle": "2023-01-22T12:41:27.326827Z", + "shell.execute_reply": "2023-01-22T12:41:27.325761Z", + "shell.execute_reply.started": "2023-01-22T12:41:27.227269Z" + }, + "id": "qDCcr1_UjZzY", + "outputId": "cc44175d-eef0-42b9-839a-4c1a0efa5ce4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# of interactions: 263874\n", + "# of interactions from users with at least 5 interactions: 142670\n" + ] + } + ], + "source": [ + "print('# of interactions: %d' % len(interactions_df))\n", + "interactions_from_selected_users_df = interactions_df.merge(users_with_enough_interactions_df, \n", + " how = 'right',\n", + " left_on = 'user_id',\n", + " right_on = 'user_id')\n", + "print('# of interactions from users with at least 5 interactions: %d' % len(interactions_from_selected_users_df))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bs9IdB8fjZzY", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:41:30.431311Z", + "iopub.status.busy": "2023-01-22T12:41:30.430823Z", + "iopub.status.idle": "2023-01-22T12:41:30.436607Z", + "shell.execute_reply": "2023-01-22T12:41:30.435654Z", + "shell.execute_reply.started": "2023-01-22T12:41:30.431275Z" + }, + "id": "bs9IdB8fjZzY" + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "MTW_Y4iOjZzY", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 376 + }, + "execution": { + "iopub.execute_input": "2023-01-22T12:41:32.237281Z", + "iopub.status.busy": "2023-01-22T12:41:32.236079Z", + "iopub.status.idle": "2023-01-22T12:41:32.403346Z", + "shell.execute_reply": "2023-01-22T12:41:32.401909Z", + "shell.execute_reply.started": "2023-01-22T12:41:32.237217Z" + }, + "id": "MTW_Y4iOjZzY", + "outputId": "8027a8a8-0bf9-4c97-9b69-5281653709c9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# of unique user/item interactions: 142670\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idwatched_pct
0218496.375039
12143456.658211
221102836.658211
321122616.658211
421159976.658211
5329526.044394
63243824.954196
73248076.658211
832104366.658211
932121326.658211
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + " user_id item_id watched_pct\n", + "0 21 849 6.375039\n", + "1 21 4345 6.658211\n", + "2 21 10283 6.658211\n", + "3 21 12261 6.658211\n", + "4 21 15997 6.658211\n", + "5 32 952 6.044394\n", + "6 32 4382 4.954196\n", + "7 32 4807 6.658211\n", + "8 32 10436 6.658211\n", + "9 32 12132 6.658211" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def smooth_user_preference(x):\n", + " return math.log(1+x, 2)\n", + " \n", + "interactions_full_df = interactions_from_selected_users_df \\\n", + " .groupby(['user_id', 'item_id'])['watched_pct'].sum() \\\n", + " .apply(smooth_user_preference).reset_index()\n", + "print('# of unique user/item interactions: %d' % len(interactions_full_df))\n", + "interactions_full_df.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "wNyqdsCxjZzZ", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "execution": { + "iopub.execute_input": "2023-01-22T12:41:34.443808Z", + "iopub.status.busy": "2023-01-22T12:41:34.443346Z", + "iopub.status.idle": "2023-01-22T12:41:34.651267Z", + "shell.execute_reply": "2023-01-22T12:41:34.650080Z", + "shell.execute_reply.started": "2023-01-22T12:41:34.443774Z" + }, + "id": "wNyqdsCxjZzZ", + "outputId": "e2a2e169-78ef-4f8e-c099-eb56de49338e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# interactions on Train set: 114136\n", + "# interactions on Test set: 28534\n" + ] + } + ], + "source": [ + "interactions_train_df, interactions_test_df = train_test_split(interactions_full_df,\n", + " stratify=interactions_full_df['user_id'], \n", + " test_size=0.20,\n", + " random_state=42)\n", + "\n", + "print('# interactions on Train set: %d' % len(interactions_train_df))\n", + "print('# interactions on Test set: %d' % len(interactions_test_df))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "v1M9fBagjZzZ", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:41:38.570246Z", + "iopub.status.busy": "2023-01-22T12:41:38.568905Z", + "iopub.status.idle": "2023-01-22T12:41:38.583223Z", + "shell.execute_reply": "2023-01-22T12:41:38.581705Z", + "shell.execute_reply.started": "2023-01-22T12:41:38.570182Z" + }, + "id": "v1M9fBagjZzZ" + }, + "outputs": [], + "source": [ + "#Indexing by personId to speed up the searches during evaluation\n", + "interactions_full_indexed_df = interactions_full_df.set_index('user_id')\n", + "interactions_train_indexed_df = interactions_train_df.set_index('user_id')\n", + "interactions_test_indexed_df = interactions_test_df.set_index('user_id')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "Ra2TntFUjZzZ", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:41:42.934656Z", + "iopub.status.busy": "2023-01-22T12:41:42.934139Z", + "iopub.status.idle": "2023-01-22T12:41:42.940917Z", + "shell.execute_reply": "2023-01-22T12:41:42.939611Z", + "shell.execute_reply.started": "2023-01-22T12:41:42.934617Z" + }, + "id": "Ra2TntFUjZzZ" + }, + "outputs": [], + "source": [ + "def get_items_interacted(person_id, interactions_df):\n", + " # Get the user's data and merge in the movie information.\n", + " interacted_items = interactions_df.loc[person_id]['item_id']\n", + " return set(interacted_items if type(interacted_items) == pd.Series else [interacted_items])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "xpP7YjhRjZzZ", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:41:53.435832Z", + "iopub.status.busy": "2023-01-22T12:41:53.435366Z", + "iopub.status.idle": "2023-01-22T12:41:53.455616Z", + "shell.execute_reply": "2023-01-22T12:41:53.454525Z", + "shell.execute_reply.started": "2023-01-22T12:41:53.435796Z" + }, + "id": "xpP7YjhRjZzZ" + }, + "outputs": [], + "source": [ + "#Top-N accuracy metrics consts\n", + "EVAL_RANDOM_SAMPLE_NON_INTERACTED_ITEMS = 100\n", + "\n", + "class ModelEvaluator:\n", + "\n", + " def get_not_interacted_items_sample(self, person_id, sample_size, seed=42):\n", + " interacted_items = get_items_interacted(person_id, interactions_full_indexed_df)\n", + " all_items = set(articles_df['item_id'])\n", + " non_interacted_items = all_items - interacted_items\n", + "\n", + " random.seed(seed)\n", + " non_interacted_items_sample = random.sample(non_interacted_items, sample_size)\n", + " return set(non_interacted_items_sample)\n", + "\n", + " def _verify_hit_top_n(self, item_id, recommended_items, topn): \n", + " try:\n", + " index = next(i for i, c in enumerate(recommended_items) if c == item_id)\n", + " except:\n", + " index = -1\n", + " hit = int(index in range(0, topn))\n", + " return hit, index\n", + "\n", + " def evaluate_model_for_user(self, model, person_id):\n", + " #Getting the items in test set\n", + " interacted_values_testset = interactions_test_indexed_df.loc[person_id]\n", + " if type(interacted_values_testset['item_id']) == pd.Series:\n", + " person_interacted_items_testset = set(interacted_values_testset['item_id'])\n", + " else:\n", + " person_interacted_items_testset = set([int(interacted_values_testset['item_id'])]) \n", + " interacted_items_count_testset = len(person_interacted_items_testset) \n", + "\n", + " #Getting a ranked recommendation list from a model for a given user\n", + " person_recs_df = model.recommend_items(person_id, \n", + " items_to_ignore=get_items_interacted(person_id, \n", + " interactions_train_indexed_df), \n", + " topn=10000000000)\n", + "\n", + " hits_at_5_count = 0\n", + " hits_at_10_count = 0\n", + " #For each item the user has interacted in test set\n", + " for item_id in person_interacted_items_testset:\n", + " #Getting a random sample (100) items the user has not interacted \n", + " #(to represent items that are assumed to be no relevant to the user)\n", + " non_interacted_items_sample = self.get_not_interacted_items_sample(person_id, \n", + " sample_size=EVAL_RANDOM_SAMPLE_NON_INTERACTED_ITEMS, \n", + " seed=item_id%(2**32))\n", + "\n", + " #Combining the current interacted item with the 100 random items\n", + " items_to_filter_recs = non_interacted_items_sample.union(set([item_id]))\n", + "\n", + " #Filtering only recommendations that are either the interacted item or from a random sample of 100 non-interacted items\n", + " valid_recs_df = person_recs_df[person_recs_df['item_id'].isin(items_to_filter_recs)] \n", + " valid_recs = valid_recs_df['item_id'].values\n", + " #Verifying if the current interacted item is among the Top-N recommended items\n", + " hit_at_5, index_at_5 = self._verify_hit_top_n(item_id, valid_recs, 5)\n", + " hits_at_5_count += hit_at_5\n", + " hit_at_10, index_at_10 = self._verify_hit_top_n(item_id, valid_recs, 10)\n", + " hits_at_10_count += hit_at_10\n", + "\n", + " #Recall is the rate of the interacted items that are ranked among the Top-N recommended items, \n", + " #when mixed with a set of non-relevant items\n", + " recall_at_5 = hits_at_5_count / float(interacted_items_count_testset)\n", + " recall_at_10 = hits_at_10_count / float(interacted_items_count_testset)\n", + "\n", + " person_metrics = {'hits@5_count':hits_at_5_count, \n", + " 'hits@10_count':hits_at_10_count, \n", + " 'interacted_count': interacted_items_count_testset,\n", + " 'recall@5': recall_at_5,\n", + " 'recall@10': recall_at_10}\n", + " return person_metrics\n", + "\n", + " def evaluate_model(self, model):\n", + " #print('Running evaluation for users')\n", + " people_metrics = []\n", + " for idx, person_id in enumerate(tqdm(list(interactions_test_indexed_df.index.unique().values))):\n", + " #if idx % 100 == 0 and idx > 0:\n", + " # print('%d users processed' % idx)\n", + " person_metrics = self.evaluate_model_for_user(model, person_id) \n", + " person_metrics['user_id'] = person_id\n", + " people_metrics.append(person_metrics)\n", + " print('%d users processed' % idx)\n", + "\n", + " detailed_results_df = pd.DataFrame(people_metrics) \\\n", + " .sort_values('interacted_count', ascending=False)\n", + " \n", + " global_recall_at_5 = detailed_results_df['hits@5_count'].sum() / float(detailed_results_df['interacted_count'].sum())\n", + " global_recall_at_10 = detailed_results_df['hits@10_count'].sum() / float(detailed_results_df['interacted_count'].sum())\n", + " \n", + " global_metrics = {'modelName': model.get_model_name(),\n", + " 'recall@5': global_recall_at_5,\n", + " 'recall@10': global_recall_at_10} \n", + " return global_metrics, detailed_results_df\n", + " \n", + "model_evaluator = ModelEvaluator() " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "bt-Ko_HMjZza", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:41:57.779034Z", + "iopub.status.busy": "2023-01-22T12:41:57.777417Z", + "iopub.status.idle": "2023-01-22T12:41:57.787389Z", + "shell.execute_reply": "2023-01-22T12:41:57.785909Z", + "shell.execute_reply.started": "2023-01-22T12:41:57.778960Z" + }, + "id": "bt-Ko_HMjZza" + }, + "outputs": [], + "source": [ + "from IPython.display import display, clear_output\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "from tqdm.notebook import tqdm\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "import torch\n", + "from torch import nn\n", + "from torch.nn import functional as F\n", + "from torch.utils.data import Dataset, DataLoader" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "6ySqiCo5jZza", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:42:03.271305Z", + "iopub.status.busy": "2023-01-22T12:42:03.270810Z", + "iopub.status.idle": "2023-01-22T12:42:03.278535Z", + "shell.execute_reply": "2023-01-22T12:42:03.277141Z", + "shell.execute_reply.started": "2023-01-22T12:42:03.271268Z" + }, + "id": "6ySqiCo5jZza" + }, + "outputs": [], + "source": [ + "\n", + "# Constants\n", + "SEED = 42 # random seed for reproducibility\n", + "LR = 1e-3 # learning rate, controls the speed of the training\n", + "WEIGHT_DECAY = 0.01 # lambda for L2 reg. ()\n", + "NUM_EPOCHS = 200 # num training epochs (how many times each instance will be processed)\n", + "GAMMA = 0.9995 # learning rate scheduler parameter\n", + "BATCH_SIZE = 3000 # training batch size\n", + "EVAL_BATCH_SIZE = 3000 # evaluation batch size.\n", + "DEVICE = 'cuda' #'cuda' # device to make the calculations on" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "FtzzvibljZza", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:42:05.933060Z", + "iopub.status.busy": "2023-01-22T12:42:05.931911Z", + "iopub.status.idle": "2023-01-22T12:42:05.969002Z", + "shell.execute_reply": "2023-01-22T12:42:05.967458Z", + "shell.execute_reply.started": "2023-01-22T12:42:05.933000Z" + }, + "id": "FtzzvibljZza" + }, + "outputs": [], + "source": [ + "total_df = interactions_train_df.append(interactions_test_indexed_df.reset_index())\n", + "total_df['user_id'], users_keys = total_df.user_id.factorize()\n", + "total_df['item_id'], items_keys = total_df.item_id.factorize()\n", + "\n", + "train_encoded = total_df.iloc[:len(interactions_train_df)].values\n", + "test_encoded = total_df.iloc[len(interactions_train_df):].values" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "crbEdHiJjZza", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:42:09.354000Z", + "iopub.status.busy": "2023-01-22T12:42:09.352465Z", + "iopub.status.idle": "2023-01-22T12:42:09.967185Z", + "shell.execute_reply": "2023-01-22T12:42:09.965725Z", + "shell.execute_reply.started": "2023-01-22T12:42:09.353932Z" + }, + "id": "crbEdHiJjZza" + }, + "outputs": [], + "source": [ + "from scipy.sparse import csr_matrix\n", + "shape = [int(total_df['user_id'].max()+1), int(total_df['item_id'].max()+1)]\n", + "X_train = csr_matrix((train_encoded[:, 2], (train_encoded[:, 0], train_encoded[:, 1])), shape=shape).toarray()\n", + "X_test = csr_matrix((test_encoded[:, 2], (test_encoded[:, 0], test_encoded[:, 1])), shape=shape).toarray()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "sFeJZsDJjZzb", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:42:12.745785Z", + "iopub.status.busy": "2023-01-22T12:42:12.745283Z", + "iopub.status.idle": "2023-01-22T12:42:12.754320Z", + "shell.execute_reply": "2023-01-22T12:42:12.752855Z", + "shell.execute_reply.started": "2023-01-22T12:42:12.745745Z" + }, + "id": "sFeJZsDJjZzb" + }, + "outputs": [], + "source": [ + "# Initialize the DataObject, which must return an element (features vector x and target value y)\n", + "# for a given idx. This class must also have a length atribute\n", + "class UserOrientedDataset(Dataset):\n", + " def __init__(self, X):\n", + " super().__init__() # to initialize the parent class\n", + " self.X = X.astype(np.float32)\n", + " self.len = len(X)\n", + "\n", + " def __len__(self): # We use __func__ for implementing in-built python functions\n", + " return self.len\n", + "\n", + " def __getitem__(self, index):\n", + " return self.X[index]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "AoCCUSpUjZzb", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:42:16.254953Z", + "iopub.status.busy": "2023-01-22T12:42:16.254416Z", + "iopub.status.idle": "2023-01-22T12:42:17.434704Z", + "shell.execute_reply": "2023-01-22T12:42:17.433103Z", + "shell.execute_reply.started": "2023-01-22T12:42:16.254903Z" + }, + "id": "AoCCUSpUjZzb" + }, + "outputs": [], + "source": [ + "# Initialize DataLoaders - objects, which sample instances from DataObject-s\n", + "train_dl = DataLoader(\n", + " UserOrientedDataset(X_train),\n", + " batch_size = BATCH_SIZE,\n", + " shuffle = True\n", + ")\n", + "\n", + "test_dl = DataLoader(\n", + " UserOrientedDataset(X_test),\n", + " batch_size = EVAL_BATCH_SIZE,\n", + " shuffle = False\n", + ")\n", + "\n", + "dls = {'train': train_dl, 'test': test_dl}" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "b94CXGocjZzb", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:53:12.965059Z", + "iopub.status.busy": "2023-01-22T12:53:12.964527Z", + "iopub.status.idle": "2023-01-22T12:53:12.975037Z", + "shell.execute_reply": "2023-01-22T12:53:12.973690Z", + "shell.execute_reply.started": "2023-01-22T12:53:12.965016Z" + }, + "id": "b94CXGocjZzb" + }, + "outputs": [], + "source": [ + "class Model(nn.Module):\n", + " def __init__(self, in_and_out_features = 8287):\n", + " super().__init__()\n", + " self.in_and_out_features = in_and_out_features\n", + " self.hidden_size = 500\n", + "\n", + " self.sequential = nn.Sequential( \n", + " nn.Linear(in_and_out_features, self.hidden_size), \n", + " nn.ReLU(), \n", + " nn.Linear(self.hidden_size, in_and_out_features) # Another Linear transformation\n", + " )\n", + "\n", + " def forward(self, x): # In the forward function, you define how your model runs, from input to output \n", + " x = self.sequential(x)\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "aY_vqVZLjZzb", + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:54:25.315144Z", + "iopub.status.busy": "2023-01-22T12:54:25.314623Z", + "iopub.status.idle": "2023-01-22T12:54:26.136714Z", + "shell.execute_reply": "2023-01-22T12:54:26.135715Z", + "shell.execute_reply.started": "2023-01-22T12:54:25.315101Z" + }, + "id": "aY_vqVZLjZzb" + }, + "outputs": [], + "source": [ + "torch.manual_seed(SEED) # Fix random seed to have reproducible weights of model layers\n", + "\n", + "model = Model()\n", + "model.to(DEVICE)\n", + "\n", + "# Initialize GD method, which will update the weights of the model\n", + "optimizer = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=WEIGHT_DECAY)\n", + "# Initialize learning rate scheduler, which will decrease LR according to some rule\n", + "scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=GAMMA)\n", + "\n", + "def rmse_for_sparse(x_pred, x_true):\n", + " mask = (x_true > 0)\n", + " sq_diff = (x_pred * mask - x_true) ** 2\n", + " mse = sq_diff.sum() / mask.sum()\n", + " return mse ** (1/2)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "LdlKerxfjZzb", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 419 + }, + "execution": { + "iopub.execute_input": "2023-01-22T12:54:33.544338Z", + "iopub.status.busy": "2023-01-22T12:54:33.543734Z" + }, + "id": "LdlKerxfjZzb", + "outputId": "0bc103bb-151d-449f-b7b2-f670b8970d92" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTrain RMSETest RMSE
002.3150152.295504
112.1916362.224912
221.9554972.108439
331.8361192.027701
441.7367832.026640
............
1951950.2886581.330020
1961960.2779171.331115
1971970.3070821.330125
1981980.3029801.331673
1991990.3073371.329725
\n", + "

200 rows × 3 columns

\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + " Epoch Train RMSE Test RMSE\n", + "0 0 2.315015 2.295504\n", + "1 1 2.191636 2.224912\n", + "2 2 1.955497 2.108439\n", + "3 3 1.836119 2.027701\n", + "4 4 1.736783 2.026640\n", + ".. ... ... ...\n", + "195 195 0.288658 1.330020\n", + "196 196 0.277917 1.331115\n", + "197 197 0.307082 1.330125\n", + "198 198 0.302980 1.331673\n", + "199 199 0.307337 1.329725\n", + "\n", + "[200 rows x 3 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Training loop\n", + "metrics_dict = {\n", + " \"Epoch\": [],\n", + " \"Train RMSE\": [],\n", + " \"Test RMSE\": [],\n", + "}\n", + "\n", + "# Train loop\n", + "for epoch in range(NUM_EPOCHS):\n", + " metrics_dict[\"Epoch\"].append(epoch)\n", + " for stage in ['train', 'test']:\n", + " with torch.set_grad_enabled(stage == 'train'): # Whether to start building a graph for a backward pass\n", + " if stage == 'train':\n", + " model.train() # Enable some \"special\" layers (will speak about later)\n", + " else:\n", + " model.eval() # Disable some \"special\" layers (will speak about later)\n", + "\n", + " loss_at_stage = 0 \n", + " for batch in dls[stage]:\n", + " batch = batch.to(DEVICE)\n", + " x_pred = model(batch) # forward pass: model(x_batch) -> calls forward()\n", + " loss = rmse_for_sparse(x_pred, batch) # ¡Important! y_pred is always the first arg\n", + " if stage == \"train\":\n", + " loss.backward() # Calculate the gradients of all the parameters wrt loss\n", + " optimizer.step() # Update the parameters\n", + " scheduler.step()\n", + " optimizer.zero_grad() # Zero the saved gradient\n", + " loss_at_stage += loss.item() * len(batch)\n", + " rmse_at_stage = (loss_at_stage / len(dls[stage].dataset)) ** (1/2)\n", + " metrics_dict[f\"{stage.title()} RMSE\"].append(rmse_at_stage)\n", + " \n", + " if (epoch == NUM_EPOCHS - 1) or epoch % 10 == 9:\n", + " clear_output(wait=True)\n", + " display(pd.DataFrame(metrics_dict))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "ZXCPjyMajZzb", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ZXCPjyMajZzb", + "outputId": "a0448c4f-5e53-409b-c277-fc704e617202" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[ 0.3084, 2.5601, 1.0144, ..., -0.0948, -0.1467, 0.3106],\n", + " [ 0.1575, 0.8934, 0.1315, ..., -0.1049, 0.0096, 0.0350],\n", + " [ 0.6704, 1.5142, 0.6962, ..., -0.2259, -0.0353, 0.0676],\n", + " ...,\n", + " [ 0.3153, 1.1243, 0.1393, ..., -0.1222, -0.1398, 0.0617],\n", + " [ 0.3214, 1.9313, 0.3253, ..., -0.1548, -0.0918, -0.0392],\n", + " [ 0.3434, 0.9318, -0.0341, ..., -0.1714, -0.0446, 0.1267]],\n", + " device='cuda:0')" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "with torch.no_grad():\n", + " X_pred = model(torch.Tensor(X_test).to(DEVICE))\n", + "X_pred" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "bkSfO9fgjZzc", + "metadata": { + "id": "bkSfO9fgjZzc" + }, + "outputs": [], + "source": [ + "class AERecommender:\n", + " \n", + " MODEL_NAME = 'Autoencoder'\n", + " \n", + " def __init__(self, X_preds, X_train_and_val, X_test):\n", + "\n", + " self.X_preds = X_preds.cpu().detach().numpy()\n", + " self.X_train_and_val = X_train_and_val\n", + " self.X_test = X_test\n", + " \n", + " def get_model_name(self):\n", + " return self.MODEL_NAME\n", + " \n", + " def recommend_items(self, user_id, items_to_select_idx, topn=10, verbose=False):\n", + " user_preds = self.X_preds[user_id][items_to_select_idx]\n", + " items_idx = items_to_select_idx[np.argsort(-user_preds)[:topn]]\n", + "\n", + " # Recommend the highest predicted rating movies that the user hasn't seen yet.\n", + " return items_idx\n", + "\n", + " def evaluate(self, size=100):\n", + "\n", + " X_total = self.X_train_and_val + self.X_test\n", + "\n", + " true_5 = []\n", + " true_10 = []\n", + "\n", + " for user_id in range(len(X_test)):\n", + " non_zero = np.argwhere(self.X_test[user_id] > 0).ravel()\n", + " all_nonzero = np.argwhere(X_total[user_id] > 0).ravel()\n", + " select_from = np.setdiff1d(np.arange(X_total.shape[1]), all_nonzero)\n", + "\n", + " for non_zero_idx in non_zero:\n", + " random_non_interacted_100_items = np.random.choice(select_from, size=20, replace=False)\n", + " preds = self.recommend_items(user_id, np.append(random_non_interacted_100_items, non_zero_idx), topn=10)\n", + " true_5.append(non_zero_idx in preds[:5])\n", + " true_10.append(non_zero_idx in preds)\n", + "\n", + " return {\"recall@5\": np.mean(true_5), \"recall@10\": np.mean(true_10)}\n", + " \n", + "ae_recommender_model = AERecommender(X_pred, X_train, X_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "yRBbD9xmjZzc", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yRBbD9xmjZzc", + "outputId": "d407d2b7-ee44-4299-9b29-046f41deb396" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'recall@5': 0.08641891035330142, 'recall@10': 0.25274264483602643}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ae_global_metrics = ae_recommender_model.evaluate()\n", + "ae_global_metrics" + ] + }, + { + "cell_type": "markdown", + "id": "ydc-4MJn-KFM", + "metadata": { + "id": "ydc-4MJn-KFM" + }, + "source": [ + "Проведем эксперименты с моделями и гиперпараметрами" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "GZfxQH7Z-hMK", + "metadata": { + "id": "GZfxQH7Z-hMK" + }, + "outputs": [], + "source": [ + "def train_model():\n", + " torch.manual_seed(SEED) # Fix random seed to have reproducible weights of model layers\n", + "\n", + " model = Model()\n", + " model.to(DEVICE)\n", + "\n", + " # Initialize GD method, which will update the weights of the model\n", + " optimizer = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=WEIGHT_DECAY)\n", + " # Initialize learning rate scheduler, which will decrease LR according to some rule\n", + " scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=GAMMA)\n", + "\n", + "\n", + " # Training loop\n", + " metrics_dict = {\n", + " \"Epoch\": [],\n", + " \"Train RMSE\": [],\n", + " \"Test RMSE\": [],\n", + " }\n", + "\n", + " # Train loop\n", + " for epoch in range(NUM_EPOCHS):\n", + " metrics_dict[\"Epoch\"].append(epoch)\n", + " for stage in ['train', 'test']:\n", + " with torch.set_grad_enabled(stage == 'train'): # Whether to start building a graph for a backward pass\n", + " if stage == 'train':\n", + " model.train() # Enable some \"special\" layers (will speak about later)\n", + " else:\n", + " model.eval() # Disable some \"special\" layers (will speak about later)\n", + "\n", + " loss_at_stage = 0 \n", + " for batch in dls[stage]:\n", + " batch = batch.to(DEVICE)\n", + " x_pred = model(batch) # forward pass: model(x_batch) -> calls forward()\n", + " loss = rmse_for_sparse(x_pred, batch) # ¡Important! y_pred is always the first arg\n", + " if stage == \"train\":\n", + " loss.backward() # Calculate the gradients of all the parameters wrt loss\n", + " optimizer.step() # Update the parameters\n", + " scheduler.step()\n", + " optimizer.zero_grad() # Zero the saved gradient\n", + " loss_at_stage += loss.item() * len(batch)\n", + " rmse_at_stage = (loss_at_stage / len(dls[stage].dataset)) ** (1/2)\n", + " metrics_dict[f\"{stage.title()} RMSE\"].append(rmse_at_stage)\n", + " \n", + " with torch.no_grad():\n", + " X_pred = model(torch.Tensor(X_test).to(DEVICE))\n", + "\n", + " ae_recommender_model = AERecommender(X_pred, X_train, X_train)\n", + "\n", + " ae_global_metrics = ae_recommender_model.evaluate()\n", + "\n", + " metrics_dict[\"recall@5\"] = ae_global_metrics[\"recall@5\"]\n", + " metrics_dict[\"recall@10\"] = ae_global_metrics[\"recall@10\"]\n", + "\n", + "\n", + " return metrics_dict" + ] + }, + { + "cell_type": "markdown", + "id": "iYS06bYkA5uD", + "metadata": { + "id": "iYS06bYkA5uD" + }, + "source": [ + "C изначальной архитектурой" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "s69HDH9P-PZl", + "metadata": { + "id": "s69HDH9P-PZl" + }, + "outputs": [], + "source": [ + "class Model(nn.Module):\n", + " def __init__(self, in_and_out_features = 8287):\n", + " super().__init__()\n", + " self.in_and_out_features = in_and_out_features\n", + " self.hidden_size = 500\n", + "\n", + " self.sequential = nn.Sequential( \n", + " nn.Linear(in_and_out_features, self.hidden_size), \n", + " nn.ReLU(), \n", + " nn.Linear(self.hidden_size, in_and_out_features) # Another Linear transformation\n", + " )\n", + "\n", + " def forward(self, x): # In the forward function, you define how your model runs, from input to output \n", + " x = self.sequential(x)\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "TytUsH6vA9Wo", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TytUsH6vA9Wo", + "outputId": "464573c4-6c3f-4b04-ac32-3ea09fb84f08" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lr:0.001 ne:0.001 bs:3000 ....\n", + "lr:0.001 ne:0.001 bs:4500 ....\n", + "lr:0.001 ne:0.001 bs:3000 ....\n", + "lr:0.001 ne:0.001 bs:4500 ....\n", + "lr:0.0003 ne:0.0003 bs:3000 ....\n", + "lr:0.0003 ne:0.0003 bs:4500 ....\n", + "lr:0.0003 ne:0.0003 bs:3000 ....\n", + "lr:0.0003 ne:0.0003 bs:4500 ....\n" + ] + } + ], + "source": [ + "first_arch_metrics = {}\n", + "\n", + "for lr in [0.001, 0.0003]:\n", + " for ne in [100, 200]:\n", + " for bs in [3000, 4500]:\n", + " \n", + " print(f\"lr:{lr} ne:{lr} bs:{bs} ....\" )\n", + "\n", + " LR = lr\n", + " NUM_EPOCHS = ne\n", + " BATCH_SIZE = bs\n", + "\n", + " first_arch_metrics[f\"lr:{lr} ne:{ne} bs:{bs}\"] = train_model()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "HnEm5GLZDAuC", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HnEm5GLZDAuC", + "outputId": "d652f3d6-c81a-4e35-e070-7350ce956120" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lr:0.001 ne:100 bs:3000 0.0856485318926931 0.24734999561176826\n", + "lr:0.001 ne:100 bs:4500 0.08339590626737009 0.2456629642992969\n", + "lr:0.001 ne:200 bs:3000 0.08698450466615308 0.2526061220708553\n", + "lr:0.001 ne:200 bs:4500 0.0867699688923128 0.2529181741055321\n", + "lr:0.0003 ne:100 bs:3000 0.08751109247467015 0.25363979443572215\n", + "lr:0.0003 ne:100 bs:4500 0.08879830711771187 0.25470272167883995\n", + "lr:0.0003 ne:200 bs:3000 0.08135781641588735 0.23005061093937415\n", + "lr:0.0003 ne:200 bs:4500 0.08193316235482266 0.23188391664310024\n" + ] + } + ], + "source": [ + "for i in first_arch_metrics.keys():\n", + " print(i, first_arch_metrics[i]['recall@5'], first_arch_metrics[i]['recall@10'])" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "ZaDleIoiPyCJ", + "metadata": { + "id": "ZaDleIoiPyCJ" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "DOeEZG5_A9p4", + "metadata": { + "id": "DOeEZG5_A9p4" + }, + "source": [ + "Усложним архитектуру" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "rTOhYgiX-fEr", + "metadata": { + "id": "rTOhYgiX-fEr" + }, + "outputs": [], + "source": [ + "class Model(nn.Module):\n", + " def __init__(self, in_and_out_features = 8287):\n", + " super().__init__()\n", + " self.in_and_out_features = in_and_out_features\n", + " self.hidden_size = 512\n", + "\n", + " self.sequential = nn.Sequential( \n", + " nn.Linear(in_and_out_features, 4096), \n", + " nn.ReLU(), \n", + "\n", + " nn.Linear(4096, self.hidden_size), \n", + " nn.ReLU(),\n", + "\n", + " nn.Linear(self.hidden_size, 4096), \n", + " nn.ReLU(), \n", + "\n", + " nn.Linear(4096, in_and_out_features) # Another Linear transformation\n", + " )\n", + "\n", + " def forward(self, x): # In the forward function, you define how your model runs, from input to output \n", + " x = self.sequential(x)\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "TPRykgiN-fNe", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TPRykgiN-fNe", + "outputId": "a32247b1-3a86-479d-aa0f-df75119e18e2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lr:0.001 ne:100 bs:3000 ....\n", + "lr:0.001 ne:100 bs:4500 ....\n", + "lr:0.001 ne:200 bs:3000 ....\n", + "lr:0.001 ne:200 bs:4500 ....\n", + "lr:0.0003 ne:100 bs:3000 ....\n", + "lr:0.0003 ne:100 bs:4500 ....\n", + "lr:0.0003 ne:200 bs:3000 ....\n", + "lr:0.0003 ne:200 bs:4500 ....\n" + ] + } + ], + "source": [ + "second_arch_metrics = {}\n", + "\n", + "for lr in [0.001, 0.0003]:\n", + " for ne in [100, 200]:\n", + " for bs in [3000, 4500]:\n", + " \n", + " print(f\"lr:{lr} ne:{ne} bs:{bs} ....\" )\n", + "\n", + " LR = lr\n", + " NUM_EPOCHS = ne\n", + " BATCH_SIZE = bs\n", + "\n", + " second_arch_metrics[f\"lr:{lr} ne:{ne} bs:{bs}\"] = train_model()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "wMGBNnslD4ax", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wMGBNnslD4ax", + "outputId": "76b97e64-342e-4ef5-b71e-f6a88c7daf36" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lr:0.001 ne:100 bs:3000 0.14852701688006476 0.363608881781037\n", + "lr:0.001 ne:100 bs:4500 0.14894633680166167 0.3632090651116074\n", + "lr:0.001 ne:200 bs:3000 0.15524588725169922 0.35925965654772934\n", + "lr:0.001 ne:200 bs:4500 0.1548265673301023 0.35853803621753927\n", + "lr:0.0003 ne:100 bs:3000 0.15456327342584375 0.37245360663890703\n", + "lr:0.0003 ne:100 bs:4500 0.15338332666972218 0.3731167172125952\n", + "lr:0.0003 ne:200 bs:3000 0.15394892098257384 0.3672852448145728\n", + "lr:0.0003 ne:200 bs:4500 0.1538026465913191 0.36697319277989604\n" + ] + } + ], + "source": [ + "for i in second_arch_metrics.keys():\n", + " print(i, second_arch_metrics[i]['recall@5'], second_arch_metrics[i]['recall@10'])" + ] + }, + { + "cell_type": "markdown", + "id": "k6zRyd-uD0Fy", + "metadata": { + "id": "k6zRyd-uD0Fy" + }, + "source": [ + "Добавим еще слоев: " + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "08GqJ7iu-fPo", + "metadata": { + "id": "08GqJ7iu-fPo" + }, + "outputs": [], + "source": [ + "class Model(nn.Module):\n", + " def __init__(self, in_and_out_features = 8287):\n", + " super().__init__()\n", + " self.in_and_out_features = in_and_out_features\n", + " self.hidden_size = 512\n", + "\n", + " self.sequential = nn.Sequential( \n", + " nn.Linear(in_and_out_features, 6000), \n", + " nn.ReLU(), \n", + "\n", + " nn.Linear(6000, 3000), \n", + " nn.ReLU(),\n", + "\n", + " nn.Linear(3000, 1024), \n", + " nn.ReLU(),\n", + "\n", + " nn.Linear(1024, self.hidden_size), \n", + " nn.ReLU(),\n", + "\n", + " nn.Linear(self.hidden_size, 1024), \n", + " nn.ReLU(),\n", + "\n", + " nn.Linear(1024, 3000), \n", + " nn.ReLU(),\n", + "\n", + " nn.Linear(3000, 6000), \n", + " nn.ReLU(), \n", + "\n", + " nn.Linear(6000, in_and_out_features) # Another Linear transformation\n", + " )\n", + "\n", + " def forward(self, x): # In the forward function, you define how your model runs, from input to output \n", + " x = self.sequential(x)\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "AV4bbBpd-fSg", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AV4bbBpd-fSg", + "outputId": "2a985b96-d452-490b-d73c-419e0341b0d8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lr:0.0003 ne:100 bs:3000 ....\n", + "lr:0.0003 ne:100 bs:4500 ....\n", + "lr:0.0003 ne:200 bs:3000 ....\n", + "lr:0.0003 ne:200 bs:4500 ....\n" + ] + } + ], + "source": [ + "third_arch_metrics = {}\n", + "\n", + "for lr in [0.0003]:\n", + " for ne in [100, 200]:\n", + " for bs in [3000, 4500]:\n", + " \n", + " print(f\"lr:{lr} ne:{ne} bs:{bs} ....\" )\n", + "\n", + " LR = lr\n", + " NUM_EPOCHS = ne\n", + " BATCH_SIZE = bs\n", + "\n", + " third_arch_metrics[f\"lr:{lr} ne:{ne} bs:{bs}\"] = train_model()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "v1gCEb8aFQc-", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "v1gCEb8aFQc-", + "outputId": "f7084cf6-b161-4951-cc6d-1abe32766205" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lr:0.0003 ne:100 bs:3000 0.24635532975123603 0.6131237383833754\n", + "lr:0.0003 ne:100 bs:4500 0.2430007703784606 0.6135430583049724\n", + "lr:0.0003 ne:200 bs:3000 0.2589251757730602 0.6017533423698401\n", + "lr:0.0003 ne:200 bs:4500 0.2589739339034784 0.6040157196212469\n" + ] + } + ], + "source": [ + "for i in third_arch_metrics.keys():\n", + " print(i, third_arch_metrics[i]['recall@5'], third_arch_metrics[i]['recall@10'])" + ] + }, + { + "cell_type": "markdown", + "id": "k0qGe8sVaZq4", + "metadata": { + "id": "k0qGe8sVaZq4" + }, + "source": [ + "Модель обучена. Лучшей моделью является модель последней архитектуры , со следующими подобранными гипперпараметрам:\n", + "\n", + "* LR: 0.0003\n", + "* NUM_EPOCHS: 200\n", + "* BATCH_SIZE: 4500\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sd7WVXXYo7H1", + "metadata": { + "id": "sd7WVXXYo7H1" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/hw_5_dssm.ipynb b/hw_5_dssm.ipynb new file mode 100644 index 00000000..258bae7e --- /dev/null +++ b/hw_5_dssm.ipynb @@ -0,0 +1,4034 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:22.841107Z", + "iopub.status.busy": "2023-01-22T16:23:22.840365Z", + "iopub.status.idle": "2023-01-22T16:23:22.850076Z", + "shell.execute_reply": "2023-01-22T16:23:22.848844Z", + "shell.execute_reply.started": "2023-01-22T16:23:22.841044Z" + } + }, + "outputs": [], + "source": [ + "import ast\n", + "import json\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import os\n", + "import pandas as pd\n", + "import pickle\n", + "import tensorflow as tf\n", + "import tensorflow.keras.backend as K\n", + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "\n", + "from collections import Counter\n", + "from random import randint, random\n", + "from scipy.sparse import coo_matrix, hstack\n", + "from sklearn.metrics.pairwise import euclidean_distances, cosine_distances, cosine_similarity\n", + "from sklearn.metrics.pairwise import euclidean_distances as ED\n", + "from tensorflow import keras\n", + "from tqdm import tqdm" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:39:51.661446Z", + "start_time": "2021-10-28T18:39:51.563879Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:22.852847Z", + "iopub.status.busy": "2023-01-22T16:23:22.851743Z", + "iopub.status.idle": "2023-01-22T16:23:29.088896Z", + "shell.execute_reply": "2023-01-22T16:23:29.087873Z", + "shell.execute_reply.started": "2023-01-22T16:23:22.852800Z" + }, + "id": "25508632" + }, + "outputs": [], + "source": [ + "interactions_df = pd.read_csv('interactions_processed_kion.csv')\n", + "users_df = pd.read_csv('users_processed_kion.csv')\n", + "items_df = pd.read_csv('items_processed_kion.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:35.447336Z", + "start_time": "2021-10-28T18:40:35.434541Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.097384Z", + "iopub.status.busy": "2023-01-22T16:23:29.094877Z", + "iopub.status.idle": "2023-01-22T16:23:29.123826Z", + "shell.execute_reply": "2023-01-22T16:23:29.123005Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.097341Z" + }, + "id": "f5eacb31", + "outputId": "37b5c35b-4f4b-48ea-9012-a6ce7eed31c7" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_idageincomesexkids_flg
0973171age_25_34income_60_90MTrue
1962099age_18_24income_20_40MFalse
21047345age_45_54income_40_60FFalse
3721985age_45_54income_20_40FFalse
4704055age_35_44income_60_90FFalse
\n", + "
" + ], + "text/plain": [ + " user_id age income sex kids_flg\n", + "0 973171 age_25_34 income_60_90 M True\n", + "1 962099 age_18_24 income_20_40 M False\n", + "2 1047345 age_45_54 income_40_60 F False\n", + "3 721985 age_45_54 income_20_40 F False\n", + "4 704055 age_35_44 income_60_90 F False" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:36.103997Z", + "start_time": "2021-10-28T18:40:36.094699Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.130158Z", + "iopub.status.busy": "2023-01-22T16:23:29.127963Z", + "iopub.status.idle": "2023-01-22T16:23:29.145149Z", + "shell.execute_reply": "2023-01-22T16:23:29.144033Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.130122Z" + }, + "id": "61669d0d" + }, + "outputs": [], + "source": [ + "items_df = items_df.rename(columns = {'id' : 'item_id'})" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:36.378293Z", + "start_time": "2021-10-28T18:40:36.370946Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.146754Z", + "iopub.status.busy": "2023-01-22T16:23:29.146394Z", + "iopub.status.idle": "2023-01-22T16:23:29.166993Z", + "shell.execute_reply": "2023-01-22T16:23:29.165796Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.146717Z" + }, + "id": "25f4462e", + "outputId": "5cc6c801-f866-4b52-aada-f5226a5ebc21" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_typetitletitle_origgenrescountriesfor_kidsage_ratingstudiosdirectorsactorsdescriptionkeywordsrelease_year_cat
010711filmпоговори с нейHable con ellaдрамы, зарубежные, детективы, мелодрамыиспанияFalse16.0unknownпедро альмодоварАдольфо Фернандес, Ана Фернандес, Дарио Гранди...Мелодрама легендарного Педро Альмодовара «Пого...Поговори, ней, 2002, Испания, друзья, любовь, ...2000-2010
12508filmголые перцыSearch Partyзарубежные, приключения, комедиисшаFalse16.0unknownскот армстронгАдам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ...Уморительная современная комедия на популярную...Голые, перцы, 2014, США, друзья, свадьбы, прео...2010-2020
210716filmтактическая силаTactical Forceкриминал, зарубежные, триллеры, боевики, комедииканадаFalse16.0unknownадам п. калтрароАдриан Холмс, Даррен Шалави, Джерри Вассерман,...Профессиональный рестлер Стив Остин («Все или ...Тактическая, сила, 2011, Канада, бандиты, ганг...2010-2020
37868film45 лет45 Yearsдрамы, зарубежные, мелодрамывеликобританияFalse16.0unknownэндрю хэйАлександра Риддлстон-Барретт, Джеральдин Джейм...Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей...45, лет, 2015, Великобритания, брак, жизнь, лю...2010-2020
416268filmвсе решает мгновениеNaNдрамы, спорт, советские, мелодрамысссрFalse12.0ленфильмвиктор садовскийАлександр Абдулов, Александр Демьяненко, Алекс...Расчетливая чаровница из советского кинохита «...Все, решает, мгновение, 1978, СССР, сильные, ж...1970-1980
\n", + "
" + ], + "text/plain": [ + " item_id content_type title title_orig \\\n", + "0 10711 film поговори с ней Hable con ella \n", + "1 2508 film голые перцы Search Party \n", + "2 10716 film тактическая сила Tactical Force \n", + "3 7868 film 45 лет 45 Years \n", + "4 16268 film все решает мгновение NaN \n", + "\n", + " genres countries for_kids \\\n", + "0 драмы, зарубежные, детективы, мелодрамы испания False \n", + "1 зарубежные, приключения, комедии сша False \n", + "2 криминал, зарубежные, триллеры, боевики, комедии канада False \n", + "3 драмы, зарубежные, мелодрамы великобритания False \n", + "4 драмы, спорт, советские, мелодрамы ссср False \n", + "\n", + " age_rating studios directors \\\n", + "0 16.0 unknown педро альмодовар \n", + "1 16.0 unknown скот армстронг \n", + "2 16.0 unknown адам п. калтраро \n", + "3 16.0 unknown эндрю хэй \n", + "4 12.0 ленфильм виктор садовский \n", + "\n", + " actors \\\n", + "0 Адольфо Фернандес, Ана Фернандес, Дарио Гранди... \n", + "1 Адам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ... \n", + "2 Адриан Холмс, Даррен Шалави, Джерри Вассерман,... \n", + "3 Александра Риддлстон-Барретт, Джеральдин Джейм... \n", + "4 Александр Абдулов, Александр Демьяненко, Алекс... \n", + "\n", + " description \\\n", + "0 Мелодрама легендарного Педро Альмодовара «Пого... \n", + "1 Уморительная современная комедия на популярную... \n", + "2 Профессиональный рестлер Стив Остин («Все или ... \n", + "3 Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей... \n", + "4 Расчетливая чаровница из советского кинохита «... \n", + "\n", + " keywords release_year_cat \n", + "0 Поговори, ней, 2002, Испания, друзья, любовь, ... 2000-2010 \n", + "1 Голые, перцы, 2014, США, друзья, свадьбы, прео... 2010-2020 \n", + "2 Тактическая, сила, 2011, Канада, бандиты, ганг... 2010-2020 \n", + "3 45, лет, 2015, Великобритания, брак, жизнь, лю... 2010-2020 \n", + "4 Все, решает, мгновение, 1978, СССР, сильные, ж... 1970-1980 " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:36.607688Z", + "start_time": "2021-10-28T18:40:36.597640Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.169473Z", + "iopub.status.busy": "2023-01-22T16:23:29.168713Z", + "iopub.status.idle": "2023-01-22T16:23:29.183035Z", + "shell.execute_reply": "2023-01-22T16:23:29.181327Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.169432Z" + }, + "id": "b41964d3", + "outputId": "b4c8f3d5-e7af-4e29-d2e8-0defb6993b35" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pct
017654995062021-05-11425072
169931716592021-05-298317100
265668371072021-05-09100
386461376382021-07-0514483100
496486895062021-04-306725100
\n", + "
" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct\n", + "0 176549 9506 2021-05-11 4250 72\n", + "1 699317 1659 2021-05-29 8317 100\n", + "2 656683 7107 2021-05-09 10 0\n", + "3 864613 7638 2021-07-05 14483 100\n", + "4 964868 9506 2021-04-30 6725 100" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cd252422" + }, + "source": [ + "## Готовим фичи пользователей" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pBdccMPAr7KR" + }, + "source": [ + "Посмотрим, какие фичи в датасете фильмов являются категориальными и закодируем их с помощью one-hot encoding." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:37.156260Z", + "start_time": "2021-10-28T18:40:37.138422Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.185708Z", + "iopub.status.busy": "2023-01-22T16:23:29.184841Z", + "iopub.status.idle": "2023-01-22T16:23:29.504659Z", + "shell.execute_reply": "2023-01-22T16:23:29.503366Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.185668Z" + }, + "id": "692270ac", + "outputId": "7491ab1f-f9fb-4921-e383-7ecf5569e999" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_idage_age_18_24age_age_25_34age_age_35_44age_age_45_54age_age_55_64age_age_65_infage_age_unknownincome_income_0_20income_income_150_infincome_income_20_40income_income_40_60income_income_60_90income_income_90_150income_income_unknownsex_Fsex_Msex_sex_unknownkids_flg_Falsekids_flg_True
0973171FalseTrueFalseFalseFalseFalseFalseFalseFalseFalseFalseTrueFalseFalseFalseTrueFalseFalseTrue
1962099TrueFalseFalseFalseFalseFalseFalseFalseFalseTrueFalseFalseFalseFalseFalseTrueFalseTrueFalse
21047345FalseFalseFalseTrueFalseFalseFalseFalseFalseFalseTrueFalseFalseFalseTrueFalseFalseTrueFalse
3721985FalseFalseFalseTrueFalseFalseFalseFalseFalseTrueFalseFalseFalseFalseTrueFalseFalseTrueFalse
4704055FalseFalseTrueFalseFalseFalseFalseFalseFalseFalseFalseTrueFalseFalseTrueFalseFalseTrueFalse
\n", + "
" + ], + "text/plain": [ + " user_id age_age_18_24 age_age_25_34 age_age_35_44 age_age_45_54 \\\n", + "0 973171 False True False False \n", + "1 962099 True False False False \n", + "2 1047345 False False False True \n", + "3 721985 False False False True \n", + "4 704055 False False True False \n", + "\n", + " age_age_55_64 age_age_65_inf age_age_unknown income_income_0_20 \\\n", + "0 False False False False \n", + "1 False False False False \n", + "2 False False False False \n", + "3 False False False False \n", + "4 False False False False \n", + "\n", + " income_income_150_inf income_income_20_40 income_income_40_60 \\\n", + "0 False False False \n", + "1 False True False \n", + "2 False False True \n", + "3 False True False \n", + "4 False False False \n", + "\n", + " income_income_60_90 income_income_90_150 income_income_unknown sex_F \\\n", + "0 True False False False \n", + "1 False False False False \n", + "2 False False False True \n", + "3 False False False True \n", + "4 True False False True \n", + "\n", + " sex_M sex_sex_unknown kids_flg_False kids_flg_True \n", + "0 True False False True \n", + "1 True False True False \n", + "2 False False True False \n", + "3 False False True False \n", + "4 False False True False " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_cat_feats = [\"age\", \"income\", \"sex\", \"kids_flg\"]\n", + "# из исходного датафрейма оставим только item_id - этот признак нам понадобится позже\n", + "# для того, чтобы маппить айтемы из датафрейма с фильмами с айтемами \n", + "# из датафрейма с взаимодействиями\n", + "users_ohe_df = users_df.user_id\n", + "for feat in user_cat_feats:\n", + " # получаем датафрейм с one-hot encoding для каждой категориальной фичи\n", + " ohe_feat_df = pd.get_dummies(users_df[feat], prefix=feat)\n", + " # конкатенируем ohe-hot датафрейм с датафреймом, \n", + " # который мы получили на предыдущем шаге\n", + " users_ohe_df = pd.concat([users_ohe_df, ohe_feat_df], axis=1)\n", + "\n", + "users_ohe_df.head()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "74cdbd93" + }, + "source": [ + "## Готовим фичи айтемов" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5kHzJ91Mr35c" + }, + "source": [ + "Кодируем их точно так же - one-hot'ом." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.507174Z", + "iopub.status.busy": "2023-01-22T16:23:29.506716Z", + "iopub.status.idle": "2023-01-22T16:23:29.528115Z", + "shell.execute_reply": "2023-01-22T16:23:29.526826Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.507133Z" + }, + "id": "-2Wd9upSsCle", + "outputId": "671c2446-81f5-4e32-e24f-3aec9c8a2076" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_typetitletitle_origgenrescountriesfor_kidsage_ratingstudiosdirectorsactorsdescriptionkeywordsrelease_year_cat
010711filmпоговори с нейHable con ellaдрамы, зарубежные, детективы, мелодрамыиспанияFalse16.0unknownпедро альмодоварАдольфо Фернандес, Ана Фернандес, Дарио Гранди...Мелодрама легендарного Педро Альмодовара «Пого...Поговори, ней, 2002, Испания, друзья, любовь, ...2000-2010
12508filmголые перцыSearch Partyзарубежные, приключения, комедиисшаFalse16.0unknownскот армстронгАдам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ...Уморительная современная комедия на популярную...Голые, перцы, 2014, США, друзья, свадьбы, прео...2010-2020
210716filmтактическая силаTactical Forceкриминал, зарубежные, триллеры, боевики, комедииканадаFalse16.0unknownадам п. калтрароАдриан Холмс, Даррен Шалави, Джерри Вассерман,...Профессиональный рестлер Стив Остин («Все или ...Тактическая, сила, 2011, Канада, бандиты, ганг...2010-2020
37868film45 лет45 Yearsдрамы, зарубежные, мелодрамывеликобританияFalse16.0unknownэндрю хэйАлександра Риддлстон-Барретт, Джеральдин Джейм...Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей...45, лет, 2015, Великобритания, брак, жизнь, лю...2010-2020
416268filmвсе решает мгновениеNaNдрамы, спорт, советские, мелодрамысссрFalse12.0ленфильмвиктор садовскийАлександр Абдулов, Александр Демьяненко, Алекс...Расчетливая чаровница из советского кинохита «...Все, решает, мгновение, 1978, СССР, сильные, ж...1970-1980
\n", + "
" + ], + "text/plain": [ + " item_id content_type title title_orig \\\n", + "0 10711 film поговори с ней Hable con ella \n", + "1 2508 film голые перцы Search Party \n", + "2 10716 film тактическая сила Tactical Force \n", + "3 7868 film 45 лет 45 Years \n", + "4 16268 film все решает мгновение NaN \n", + "\n", + " genres countries for_kids \\\n", + "0 драмы, зарубежные, детективы, мелодрамы испания False \n", + "1 зарубежные, приключения, комедии сша False \n", + "2 криминал, зарубежные, триллеры, боевики, комедии канада False \n", + "3 драмы, зарубежные, мелодрамы великобритания False \n", + "4 драмы, спорт, советские, мелодрамы ссср False \n", + "\n", + " age_rating studios directors \\\n", + "0 16.0 unknown педро альмодовар \n", + "1 16.0 unknown скот армстронг \n", + "2 16.0 unknown адам п. калтраро \n", + "3 16.0 unknown эндрю хэй \n", + "4 12.0 ленфильм виктор садовский \n", + "\n", + " actors \\\n", + "0 Адольфо Фернандес, Ана Фернандес, Дарио Гранди... \n", + "1 Адам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ... \n", + "2 Адриан Холмс, Даррен Шалави, Джерри Вассерман,... \n", + "3 Александра Риддлстон-Барретт, Джеральдин Джейм... \n", + "4 Александр Абдулов, Александр Демьяненко, Алекс... \n", + "\n", + " description \\\n", + "0 Мелодрама легендарного Педро Альмодовара «Пого... \n", + "1 Уморительная современная комедия на популярную... \n", + "2 Профессиональный рестлер Стив Остин («Все или ... \n", + "3 Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей... \n", + "4 Расчетливая чаровница из советского кинохита «... \n", + "\n", + " keywords release_year_cat \n", + "0 Поговори, ней, 2002, Испания, друзья, любовь, ... 2000-2010 \n", + "1 Голые, перцы, 2014, США, друзья, свадьбы, прео... 2010-2020 \n", + "2 Тактическая, сила, 2011, Канада, бандиты, ганг... 2010-2020 \n", + "3 45, лет, 2015, Великобритания, брак, жизнь, лю... 2010-2020 \n", + "4 Все, решает, мгновение, 1978, СССР, сильные, ж... 1970-1980 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:37.792147Z", + "start_time": "2021-10-28T18:40:37.537501Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:29.534806Z", + "iopub.status.busy": "2023-01-22T16:23:29.533869Z", + "iopub.status.idle": "2023-01-22T16:23:30.291045Z", + "shell.execute_reply": "2023-01-22T16:23:30.289998Z", + "shell.execute_reply.started": "2023-01-22T16:23:29.534762Z" + }, + "id": "7a94ef7e", + "outputId": "1ea7a769-8c2d-43d5-f2cb-47500bc0a7ba" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_type_filmcontent_type_seriesrelease_year_cat_1920-1930release_year_cat_1930-1940release_year_cat_1940-1950release_year_cat_1950-1960release_year_cat_1960-1970release_year_cat_1970-1980release_year_cat_1980-1990...directors_ярив хоровицdirectors_ярон зильберманdirectors_ярополк лапшинdirectors_ярослав лупийdirectors_ярроу чейни, скотт моужерdirectors_ясина сезарdirectors_ясуоми умэцуdirectors_ёдзи фукуяма, ацуко фукусима, николас де креси, синъитиро ватанабэ, сёдзи кавамориdirectors_ёлкин туйчиевdirectors_ён сан-хо
010711TrueFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
12508TrueFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
210716TrueFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
37868TrueFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
416268TrueFalseFalseFalseFalseFalseFalseTrueFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
\n", + "

5 rows × 8589 columns

\n", + "
" + ], + "text/plain": [ + " item_id content_type_film content_type_series \\\n", + "0 10711 True False \n", + "1 2508 True False \n", + "2 10716 True False \n", + "3 7868 True False \n", + "4 16268 True False \n", + "\n", + " release_year_cat_1920-1930 release_year_cat_1930-1940 \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " release_year_cat_1940-1950 release_year_cat_1950-1960 \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " release_year_cat_1960-1970 release_year_cat_1970-1980 \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False True \n", + "\n", + " release_year_cat_1980-1990 ... directors_ярив хоровиц \\\n", + "0 False ... False \n", + "1 False ... False \n", + "2 False ... False \n", + "3 False ... False \n", + "4 False ... False \n", + "\n", + " directors_ярон зильберман directors_ярополк лапшин \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " directors_ярослав лупий directors_ярроу чейни, скотт моужер \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " directors_ясина сезар directors_ясуоми умэцу \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " directors_ёдзи фукуяма, ацуко фукусима, николас де креси, синъитиро ватанабэ, сёдзи кавамори \\\n", + "0 False \n", + "1 False \n", + "2 False \n", + "3 False \n", + "4 False \n", + "\n", + " directors_ёлкин туйчиев directors_ён сан-хо \n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + "[5 rows x 8589 columns]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "item_cat_feats = ['content_type', 'release_year_cat',\n", + " 'for_kids', 'age_rating', \n", + " 'studios', 'countries', 'directors']\n", + "\n", + "items_ohe_df = items_df.item_id\n", + "\n", + "for feat in item_cat_feats:\n", + " ohe_feat_df = pd.get_dummies(items_df[feat], prefix=feat)\n", + " items_ohe_df = pd.concat([items_ohe_df, ohe_feat_df], axis=1) \n", + "\n", + "items_ohe_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:30.294678Z", + "iopub.status.busy": "2023-01-22T16:23:30.294379Z", + "iopub.status.idle": "2023-01-22T16:23:30.316137Z", + "shell.execute_reply": "2023-01-22T16:23:30.314916Z", + "shell.execute_reply.started": "2023-01-22T16:23:30.294651Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_typetitletitle_origgenrescountriesfor_kidsage_ratingstudiosdirectorsactorsdescriptionkeywordsrelease_year_cat
010711filmпоговори с нейHable con ellaдрамы, зарубежные, детективы, мелодрамыиспанияFalse16.0unknownпедро альмодоварАдольфо Фернандес, Ана Фернандес, Дарио Гранди...Мелодрама легендарного Педро Альмодовара «Пого...Поговори, ней, 2002, Испания, друзья, любовь, ...2000-2010
12508filmголые перцыSearch Partyзарубежные, приключения, комедиисшаFalse16.0unknownскот армстронгАдам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ...Уморительная современная комедия на популярную...Голые, перцы, 2014, США, друзья, свадьбы, прео...2010-2020
210716filmтактическая силаTactical Forceкриминал, зарубежные, триллеры, боевики, комедииканадаFalse16.0unknownадам п. калтрароАдриан Холмс, Даррен Шалави, Джерри Вассерман,...Профессиональный рестлер Стив Остин («Все или ...Тактическая, сила, 2011, Канада, бандиты, ганг...2010-2020
37868film45 лет45 Yearsдрамы, зарубежные, мелодрамывеликобританияFalse16.0unknownэндрю хэйАлександра Риддлстон-Барретт, Джеральдин Джейм...Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей...45, лет, 2015, Великобритания, брак, жизнь, лю...2010-2020
416268filmвсе решает мгновениеNaNдрамы, спорт, советские, мелодрамысссрFalse12.0ленфильмвиктор садовскийАлександр Абдулов, Александр Демьяненко, Алекс...Расчетливая чаровница из советского кинохита «...Все, решает, мгновение, 1978, СССР, сильные, ж...1970-1980
\n", + "
" + ], + "text/plain": [ + " item_id content_type title title_orig \\\n", + "0 10711 film поговори с ней Hable con ella \n", + "1 2508 film голые перцы Search Party \n", + "2 10716 film тактическая сила Tactical Force \n", + "3 7868 film 45 лет 45 Years \n", + "4 16268 film все решает мгновение NaN \n", + "\n", + " genres countries for_kids \\\n", + "0 драмы, зарубежные, детективы, мелодрамы испания False \n", + "1 зарубежные, приключения, комедии сша False \n", + "2 криминал, зарубежные, триллеры, боевики, комедии канада False \n", + "3 драмы, зарубежные, мелодрамы великобритания False \n", + "4 драмы, спорт, советские, мелодрамы ссср False \n", + "\n", + " age_rating studios directors \\\n", + "0 16.0 unknown педро альмодовар \n", + "1 16.0 unknown скот армстронг \n", + "2 16.0 unknown адам п. калтраро \n", + "3 16.0 unknown эндрю хэй \n", + "4 12.0 ленфильм виктор садовский \n", + "\n", + " actors \\\n", + "0 Адольфо Фернандес, Ана Фернандес, Дарио Гранди... \n", + "1 Адам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ... \n", + "2 Адриан Холмс, Даррен Шалави, Джерри Вассерман,... \n", + "3 Александра Риддлстон-Барретт, Джеральдин Джейм... \n", + "4 Александр Абдулов, Александр Демьяненко, Алекс... \n", + "\n", + " description \\\n", + "0 Мелодрама легендарного Педро Альмодовара «Пого... \n", + "1 Уморительная современная комедия на популярную... \n", + "2 Профессиональный рестлер Стив Остин («Все или ... \n", + "3 Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей... \n", + "4 Расчетливая чаровница из советского кинохита «... \n", + "\n", + " keywords release_year_cat \n", + "0 Поговори, ней, 2002, Испания, друзья, любовь, ... 2000-2010 \n", + "1 Голые, перцы, 2014, США, друзья, свадьбы, прео... 2010-2020 \n", + "2 Тактическая, сила, 2011, Канада, бандиты, ганг... 2010-2020 \n", + "3 45, лет, 2015, Великобритания, брак, жизнь, лю... 2010-2020 \n", + "4 Все, решает, мгновение, 1978, СССР, сильные, ж... 1970-1980 " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items_df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Добавим текстовые фичи\n", + "С помощью TFIDFVectorizer получим эмбеддинги следующих колонок: genres, description, keywords" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:30.318830Z", + "iopub.status.busy": "2023-01-22T16:23:30.318190Z", + "iopub.status.idle": "2023-01-22T16:23:30.335666Z", + "shell.execute_reply": "2023-01-22T16:23:30.334811Z", + "shell.execute_reply.started": "2023-01-22T16:23:30.318792Z" + } + }, + "outputs": [], + "source": [ + "from sklearn.feature_extraction.text import TfidfVectorizer" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:30.338779Z", + "iopub.status.busy": "2023-01-22T16:23:30.338019Z", + "iopub.status.idle": "2023-01-22T16:23:31.386164Z", + "shell.execute_reply": "2023-01-22T16:23:31.385106Z", + "shell.execute_reply.started": "2023-01-22T16:23:30.338741Z" + } + }, + "outputs": [], + "source": [ + "for column in ['genres', 'keywords']:\n", + " tv = TfidfVectorizer(max_features = 500)\n", + " t = pd.DataFrame.sparse.from_spmatrix(tv.fit_transform(items_df[column]))\n", + " t.columns = [column + '_' + str(x) for x in t.columns]\n", + " items_ohe_df = pd.concat([items_ohe_df, t], axis = 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:31.388255Z", + "iopub.status.busy": "2023-01-22T16:23:31.387847Z", + "iopub.status.idle": "2023-01-22T16:23:31.483917Z", + "shell.execute_reply": "2023-01-22T16:23:31.482751Z", + "shell.execute_reply.started": "2023-01-22T16:23:31.388214Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_type_filmcontent_type_seriesrelease_year_cat_1920-1930release_year_cat_1930-1940release_year_cat_1940-1950release_year_cat_1950-1960release_year_cat_1960-1970release_year_cat_1970-1980release_year_cat_1980-1990...keywords_490keywords_491keywords_492keywords_493keywords_494keywords_495keywords_496keywords_497keywords_498keywords_499
010711TrueFalseFalseFalseFalseFalseFalseFalseFalse...0.00.00.00.00.00.00.00.00.00.0
12508TrueFalseFalseFalseFalseFalseFalseFalseFalse...0.00.00.00.00.00.00.00.00.00.0
210716TrueFalseFalseFalseFalseFalseFalseFalseFalse...0.00.00.00.00.00.00.00.00.00.0
37868TrueFalseFalseFalseFalseFalseFalseFalseFalse...0.00.00.00.00.00.00.00.00.00.0
416268TrueFalseFalseFalseFalseFalseFalseTrueFalse...0.00.00.00.00.00.00.00.00.00.0
\n", + "

5 rows × 9197 columns

\n", + "
" + ], + "text/plain": [ + " item_id content_type_film content_type_series \\\n", + "0 10711 True False \n", + "1 2508 True False \n", + "2 10716 True False \n", + "3 7868 True False \n", + "4 16268 True False \n", + "\n", + " release_year_cat_1920-1930 release_year_cat_1930-1940 \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " release_year_cat_1940-1950 release_year_cat_1950-1960 \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " release_year_cat_1960-1970 release_year_cat_1970-1980 \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False True \n", + "\n", + " release_year_cat_1980-1990 ... keywords_490 keywords_491 keywords_492 \\\n", + "0 False ... 0.0 0.0 0.0 \n", + "1 False ... 0.0 0.0 0.0 \n", + "2 False ... 0.0 0.0 0.0 \n", + "3 False ... 0.0 0.0 0.0 \n", + "4 False ... 0.0 0.0 0.0 \n", + "\n", + " keywords_493 keywords_494 keywords_495 keywords_496 keywords_497 \\\n", + "0 0.0 0.0 0.0 0.0 0.0 \n", + "1 0.0 0.0 0.0 0.0 0.0 \n", + "2 0.0 0.0 0.0 0.0 0.0 \n", + "3 0.0 0.0 0.0 0.0 0.0 \n", + "4 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " keywords_498 keywords_499 \n", + "0 0.0 0.0 \n", + "1 0.0 0.0 \n", + "2 0.0 0.0 \n", + "3 0.0 0.0 \n", + "4 0.0 0.0 \n", + "\n", + "[5 rows x 9197 columns]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items_ohe_df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cc595c20" + }, + "source": [ + "## Сделаем матрицу взаимодействий" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:37.898427Z", + "start_time": "2021-10-28T18:40:37.864067Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:31.486206Z", + "iopub.status.busy": "2023-01-22T16:23:31.485812Z", + "iopub.status.idle": "2023-01-22T16:23:31.604748Z", + "shell.execute_reply": "2023-01-22T16:23:31.603679Z", + "shell.execute_reply.started": "2023-01-22T16:23:31.486170Z" + }, + "id": "79c9bca3", + "outputId": "6f6148e0-8de7-4ffc-82d9-cf396db1ed98" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "item_id\n", + "10440 202457\n", + "15297 193123\n", + "9728 132865\n", + "13865 122119\n", + "4151 91167\n", + " ... \n", + "8076 1\n", + "8954 1\n", + "15664 1\n", + "818 1\n", + "10542 1\n", + "Name: count, Length: 15706, dtype: int64" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df.item_id.value_counts()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YAfqm8asrBfG" + }, + "source": [ + "В датасете взаимодействий есть непопулярные фильмы и малоактивные пользователи. Кроме того, в таблице взаимодействий есть события с низким качеством взаимодействия - когда юзер начал смотреть фильм, но вскоре после начала просмотра выключил.\n", + "\n", + "Отфильтруем такие события*, малоактивных юзеров и непопулярные фильмы.\n", + "\n", + "Можете не фильтровать такие события, тогда у вас будет больше негативных примеров." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:38.103819Z", + "start_time": "2021-10-28T18:40:38.070117Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:31.606489Z", + "iopub.status.busy": "2023-01-22T16:23:31.606197Z", + "iopub.status.idle": "2023-01-22T16:23:31.985392Z", + "shell.execute_reply": "2023-01-22T16:23:31.984254Z", + "shell.execute_reply.started": "2023-01-22T16:23:31.606462Z" + }, + "id": "17334e80", + "outputId": "bfbe26dd-7778-42ad-c5dd-283635fcafa6" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "user_id\n", + "416206 1341\n", + "1010539 764\n", + "555233 685\n", + "11526 676\n", + "409259 625\n", + " ... \n", + "45493 1\n", + "615194 1\n", + "96848 1\n", + "425823 1\n", + "697262 1\n", + "Name: count, Length: 962179, dtype: int64" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df.user_id.value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:39.717096Z", + "start_time": "2021-10-28T18:40:38.759740Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:31.987509Z", + "iopub.status.busy": "2023-01-22T16:23:31.986995Z", + "iopub.status.idle": "2023-01-22T16:23:34.897911Z", + "shell.execute_reply": "2023-01-22T16:23:34.896578Z", + "shell.execute_reply.started": "2023-01-22T16:23:31.987469Z" + }, + "id": "076e4ebc", + "outputId": "85c15fd2-12bb-478c-e00f-4f2b7bbcd6ab" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "N users before: 962179\n", + "N items before: 15706\n", + "\n", + "N users after: 79515\n", + "N items after: 6901\n" + ] + } + ], + "source": [ + "print(f\"N users before: {interactions_df.user_id.nunique()}\")\n", + "print(f\"N items before: {interactions_df.item_id.nunique()}\\n\")\n", + "\n", + "# отфильтруем все события взаимодействий, в которых пользователь посмотрел\n", + "# фильм менее чем на 10 процентов\n", + "interactions_df = interactions_df[interactions_df.watched_pct > 10]\n", + "\n", + "# соберем всех пользователей, которые посмотрели \n", + "# больше 10 фильмов (можете выбрать другой порог)\n", + "valid_users = []\n", + "\n", + "c = Counter(interactions_df.user_id)\n", + "for user_id, entries in c.most_common():\n", + " if entries > 10:\n", + " valid_users.append(user_id)\n", + "\n", + "# и соберем все фильмы, которые посмотрели больше 10 пользователей\n", + "valid_items = []\n", + "\n", + "c = Counter(interactions_df.item_id)\n", + "for item_id, entries in c.most_common():\n", + " if entries > 10:\n", + " valid_items.append(item_id)\n", + "\n", + "# отбросим непопулярные фильмы и неактивных юзеров\n", + "interactions_df = interactions_df[interactions_df.user_id.isin(valid_users)]\n", + "interactions_df = interactions_df[interactions_df.item_id.isin(valid_items)]\n", + "\n", + "print(f\"N users after: {interactions_df.user_id.nunique()}\")\n", + "print(f\"N items after: {interactions_df.item_id.nunique()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a9163fb2" + }, + "source": [ + "После фильтрации может получиться так, что некоторые айтемы/юзеры есть в датасете взаимодействий, но при этом они отсутствуют в датасетах айтемов/юзеров или наоборот. Поэтому найдем id айтемов и id юзеров, которые есть во всех датасетах и оставим только их." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:40.231703Z", + "start_time": "2021-10-28T18:40:39.718626Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:34.900180Z", + "iopub.status.busy": "2023-01-22T16:23:34.899760Z", + "iopub.status.idle": "2023-01-22T16:23:36.064882Z", + "shell.execute_reply": "2023-01-22T16:23:36.063765Z", + "shell.execute_reply.started": "2023-01-22T16:23:34.900142Z" + }, + "id": "d55848e1", + "outputId": "48609a0b-06b9-4a5e-f8f6-061db1c6dcb2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "65974\n", + "6901\n" + ] + } + ], + "source": [ + "common_users = set(interactions_df.user_id.unique()).intersection(set(users_ohe_df.user_id.unique()))\n", + "common_items = set(interactions_df.item_id.unique()).intersection(set(items_ohe_df.item_id.unique()))\n", + "\n", + "print(len(common_users))\n", + "print(len(common_items))\n", + "\n", + "interactions_df = interactions_df[interactions_df.item_id.isin(common_items)]\n", + "interactions_df = interactions_df[interactions_df.user_id.isin(common_users)]\n", + "\n", + "items_ohe_df = items_ohe_df[items_ohe_df.item_id.isin(common_items)]\n", + "users_ohe_df = users_ohe_df[users_ohe_df.user_id.isin(common_users)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1e8b9480" + }, + "source": [ + "\n", + "Соберем взаимодействия в матрицу user*item так, чтобы в строках этой матрицы были user_id, в столбцах - item_id, а на пересечениях строк и столбцов - единица, если пользователь взаимодействовал с айтемом и ноль, если нет.\n", + "\n", + "Такую матрицу удобно собирать в numpy array, однако нужно помнить, что numpy array индексируется порядковыми индексами, а нам же удобнее использовать item_id и user_id.\n", + "\n", + "Создадим некие внутренние индексы для user_id и item_id - uid и iid. Для этого просто соберем все user_id и item_id и пронумеруем их по порядку." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:40:40.346587Z", + "start_time": "2021-10-28T18:40:40.233046Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:36.066990Z", + "iopub.status.busy": "2023-01-22T16:23:36.066574Z", + "iopub.status.idle": "2023-01-22T16:23:36.211726Z", + "shell.execute_reply": "2023-01-22T16:23:36.210597Z", + "shell.execute_reply.started": "2023-01-22T16:23:36.066949Z" + }, + "id": "81679fb0", + "outputId": "0c6bf7ce-1ea0-46c2-9d70-42b32bf08c7e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pctuidiid
017654995062021-05-11425072106163944
169931716592021-05-29831710042131675
610164583542021-08-1416722561024139
78840096932021-08-047031453150279
14532484372021-04-186598923103485
\n", + "
" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct uid iid\n", + "0 176549 9506 2021-05-11 4250 72 10616 3944\n", + "1 699317 1659 2021-05-29 8317 100 42131 675\n", + "6 1016458 354 2021-08-14 1672 25 61024 139\n", + "7 884009 693 2021-08-04 703 14 53150 279\n", + "14 5324 8437 2021-04-18 6598 92 310 3485" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df[\"uid\"] = interactions_df[\"user_id\"].astype(\"category\")\n", + "interactions_df[\"uid\"] = interactions_df[\"uid\"].cat.codes\n", + "\n", + "interactions_df[\"iid\"] = interactions_df[\"item_id\"].astype(\"category\")\n", + "interactions_df[\"iid\"] = interactions_df[\"iid\"].cat.codes\n", + "\n", + "print(sorted(interactions_df.iid.unique())[:5])\n", + "print(sorted(interactions_df.uid.unique())[:5])\n", + "interactions_df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "61c855e5" + }, + "source": [ + "Отнормируем матрицу взаимодействий" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:36.214161Z", + "iopub.status.busy": "2023-01-22T16:23:36.213276Z", + "iopub.status.idle": "2023-01-22T16:23:36.223246Z", + "shell.execute_reply": "2023-01-22T16:23:36.222069Z", + "shell.execute_reply.started": "2023-01-22T16:23:36.214121Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0 3944\n", + "1 675\n", + "6 139\n", + "7 279\n", + "14 3485\n", + " ... \n", + "5476218 169\n", + "5476224 923\n", + "5476226 5610\n", + "5476239 2929\n", + "5476249 6766\n", + "Name: iid, Length: 1463641, dtype: int16" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions_df.iid" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:03.360248Z", + "start_time": "2021-10-28T18:40:40.348057Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:36.225520Z", + "iopub.status.busy": "2023-01-22T16:23:36.224590Z", + "iopub.status.idle": "2023-01-22T16:23:43.629733Z", + "shell.execute_reply": "2023-01-22T16:23:43.628568Z", + "shell.execute_reply.started": "2023-01-22T16:23:36.225480Z" + }, + "id": "3feced70" + }, + "outputs": [], + "source": [ + "interactions_vec = np.zeros((interactions_df.uid.nunique(), \n", + " interactions_df.iid.nunique())) \n", + "\n", + "for user_id, item_id in zip(interactions_df.uid, interactions_df.iid):\n", + " interactions_vec[user_id, item_id] += 1\n", + "\n", + "\n", + "res = interactions_vec.sum(axis=1)\n", + "for i in range(len(interactions_vec)):\n", + " interactions_vec[i] /= res[i]" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:03.416061Z", + "start_time": "2021-10-28T18:41:03.363462Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:43.634195Z", + "iopub.status.busy": "2023-01-22T16:23:43.631362Z", + "iopub.status.idle": "2023-01-22T16:23:43.711673Z", + "shell.execute_reply": "2023-01-22T16:23:43.710586Z", + "shell.execute_reply.started": "2023-01-22T16:23:43.634161Z" + }, + "id": "9f5ec90f", + "outputId": "9acdfe45-aa4e-4a64-ffdc-1a750390ae84" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6897\n", + "6901\n", + "65974\n", + "65974\n", + "{11805, 9788, 11501, 1734}\n" + ] + } + ], + "source": [ + "print(interactions_df.item_id.nunique())\n", + "print(items_ohe_df.item_id.nunique())\n", + "print(interactions_df.user_id.nunique())\n", + "print(users_ohe_df.user_id.nunique())\n", + "\n", + "print(set(items_ohe_df.item_id.unique()) - set(interactions_df.item_id.unique()))" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:23:43.713551Z", + "iopub.status.busy": "2023-01-22T16:23:43.713196Z", + "iopub.status.idle": "2023-01-22T16:23:44.238808Z", + "shell.execute_reply": "2023-01-22T16:23:44.237691Z", + "shell.execute_reply.started": "2023-01-22T16:23:43.713517Z" + } + }, + "outputs": [], + "source": [ + "items_ohe_df = items_ohe_df[~items_ohe_df.item_id.isin([11805, 9788, 11501, 1734])]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "19e69bae" + }, + "source": [ + "Для того, чтобы можно было удобно превратить iid/uid в item_id/user_id и наоборот соберем словари \n", + "\n", + "{iid: item_id}, {uid: user_id} и {item_id: iid}, {user_id: uid}." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:03.637495Z", + "start_time": "2021-10-28T18:41:03.417544Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:44.243767Z", + "iopub.status.busy": "2023-01-22T16:23:44.243422Z", + "iopub.status.idle": "2023-01-22T16:23:44.817126Z", + "shell.execute_reply": "2023-01-22T16:23:44.816088Z", + "shell.execute_reply.started": "2023-01-22T16:23:44.243739Z" + }, + "id": "c8a84024" + }, + "outputs": [], + "source": [ + "iid_to_item_id = interactions_df[[\"iid\", \"item_id\"]].drop_duplicates().set_index(\"iid\").to_dict()[\"item_id\"]\n", + "item_id_to_iid = interactions_df[[\"iid\", \"item_id\"]].drop_duplicates().set_index(\"item_id\").to_dict()[\"iid\"]\n", + "\n", + "uid_to_user_id = interactions_df[[\"uid\", \"user_id\"]].drop_duplicates().set_index(\"uid\").to_dict()[\"user_id\"]\n", + "user_id_to_uid = interactions_df[[\"uid\", \"user_id\"]].drop_duplicates().set_index(\"user_id\").to_dict()[\"uid\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "48ca5204" + }, + "source": [ + "И проиндексируем датасеты users_ohe_df и items_ohe_df по внутренним айди:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:03.744883Z", + "start_time": "2021-10-28T18:41:03.638719Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:44.819593Z", + "iopub.status.busy": "2023-01-22T16:23:44.818859Z", + "iopub.status.idle": "2023-01-22T16:23:44.930257Z", + "shell.execute_reply": "2023-01-22T16:23:44.929032Z", + "shell.execute_reply.started": "2023-01-22T16:23:44.819553Z" + }, + "id": "4c4980ac" + }, + "outputs": [], + "source": [ + "items_ohe_df[\"iid\"] = items_ohe_df[\"item_id\"].apply(lambda x: item_id_to_iid[x])\n", + "items_ohe_df = items_ohe_df.set_index(\"iid\")\n", + "\n", + "users_ohe_df[\"uid\"] = users_ohe_df[\"user_id\"].apply(lambda x: user_id_to_uid[x])\n", + "users_ohe_df = users_ohe_df.set_index(\"uid\")" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:03.749717Z", + "start_time": "2021-10-28T18:41:03.746067Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:23:44.932306Z", + "iopub.status.busy": "2023-01-22T16:23:44.931684Z", + "iopub.status.idle": "2023-01-22T16:23:44.939719Z", + "shell.execute_reply": "2023-01-22T16:23:44.938755Z", + "shell.execute_reply.started": "2023-01-22T16:23:44.932267Z" + }, + "id": "22c26d39" + }, + "outputs": [], + "source": [ + "def triplet_loss(y_true, y_pred, n_dims=128, alpha=0.4):\n", + " # будем ожидать, что на вход функции прилетит три сконкатенированных \n", + " # вектора - вектор юзера и два вектора айтема\n", + " anchor = y_pred[:, 0:n_dims]\n", + " positive = y_pred[:, n_dims:n_dims*2]\n", + " negative = y_pred[:, n_dims*2:n_dims*3]\n", + "\n", + " # считаем расстояния от вектора юзера до вектора хорошего айтема\n", + " pos_dist = K.sum(K.square(anchor - positive), axis=1)\n", + " # и до плохого\n", + " neg_dist = K.sum(K.square(anchor - negative), axis=1)\n", + "\n", + " # считаем лосс\n", + " basic_loss = pos_dist - neg_dist + alpha\n", + " loss = K.maximum(basic_loss, 0.0) # возвращаем ноль, если лосс отрицательный\n", + " \n", + " return loss\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T19:19:05.615364Z", + "start_time": "2021-10-28T19:19:05.612463Z" + }, + "id": "4de262b4" + }, + "source": [ + "Попробуйте другие лоссы, например, BPR Triplet loss" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:18:11.520568Z", + "iopub.status.busy": "2023-01-22T16:18:11.519791Z", + "iopub.status.idle": "2023-01-22T16:18:11.535194Z", + "shell.execute_reply": "2023-01-22T16:18:11.533962Z", + "shell.execute_reply.started": "2023-01-22T16:18:11.520528Z" + } + }, + "outputs": [], + "source": [ + "def bpr_triplet_loss(y_true, y_pred, n_dims=128):\n", + " \n", + " from keras import backend as K\n", + " \n", + " anchor = y_pred[:, 0:n_dims]\n", + " positive = y_pred[:, n_dims:n_dims*2]\n", + " negative = y_pred[:, n_dims*2:n_dims*3]\n", + "\n", + " # BPR loss\n", + " loss = 1.0 - K.sigmoid(\n", + " K.sum(anchor * positive, axis=-1, keepdims=True) -\n", + " K.sum(anchor * negative, axis=-1, keepdims=True))\n", + "\n", + " return loss" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-23T11:20:03.327838Z", + "start_time": "2021-10-23T11:20:03.324389Z" + }, + "id": "85d618b6" + }, + "source": [ + "## Генератор и семплирование\n", + "\n", + "- хорошим примером будет тот айтем, который был взят из датасета взаимодействий в соответствии с распределением просмотренных айтемов для этого юзера;\n", + "- Для негативного буду рандомно брать айтем из 100 наиболее непохожих по евклидовому расстоянию на положительный айтем по вектору жанр и ключевые слова, который человек при этом не смотрел \n", + "\n", + "Т. о., если например человек посмотрел целиком триллер, то в негативный для него должно попасть что-то вроде мелодрамы, при этом ключевые слова тоже будут сильно отличаться \n", + "\n", + "\n", + "Сформируем заранее следующий словарь - для каждого айтема: список из ста наиболее непохожих айтемов. Тогда в генераторе нужно будет взять рандомное значение их ста айтемов для положительного айтема. Если считать это в моменте работы генератора, то получается чрезвычайно долго, а здесь обращение к словарю - O(1), и взятие рандомного значения такое же по сложности, как в простом генераторе\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T15:37:25.302855Z", + "iopub.status.busy": "2023-01-22T15:37:25.302472Z", + "iopub.status.idle": "2023-01-22T15:37:32.215552Z", + "shell.execute_reply": "2023-01-22T15:37:32.214488Z", + "shell.execute_reply.started": "2023-01-22T15:37:25.302820Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 6897/6897 [00:02<00:00, 2513.96it/s]\n" + ] + } + ], + "source": [ + "# формируем слоарь\n", + "\n", + "fts = items_ohe_df[[x for x in items_ohe_df if 'genre' in x or 'keywords' in x]]\n", + "\n", + "distances = pd.DataFrame(ED(fts))\n", + "distances.columns = list(fts.index)\n", + "distances.index = fts.index\n", + "\n", + "distance_dict = {}\n", + "for i in tqdm(distances.columns):\n", + " distance_dict[i] = list(distances[i].sort_values()[-100:].index)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:39:46.221189Z", + "iopub.status.busy": "2023-01-22T12:39:46.220459Z", + "iopub.status.idle": "2023-01-22T12:39:49.254755Z", + "shell.execute_reply": "2023-01-22T12:39:49.253714Z", + "shell.execute_reply.started": "2023-01-22T12:39:46.221147Z" + } + }, + "outputs": [], + "source": [ + "iids_ = np.array(fts.index)\n", + "user_interactions = interactions_df.groupby(\"uid\")['iid'].apply(lambda x: np.array(x.unique())).to_dict()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T15:37:35.885246Z", + "iopub.status.busy": "2023-01-22T15:37:35.884025Z", + "iopub.status.idle": "2023-01-22T15:37:35.891070Z", + "shell.execute_reply": "2023-01-22T15:37:35.889851Z", + "shell.execute_reply.started": "2023-01-22T15:37:35.885203Z" + } + }, + "outputs": [], + "source": [ + "def get_negative_sample(pos_i, uid_i, distance_dict):\n", + " \n", + " neg_i = np.random.choice(distance_dict[pos_i])\n", + " \n", + " return neg_i" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T12:39:49.263942Z", + "iopub.status.busy": "2023-01-22T12:39:49.263310Z", + "iopub.status.idle": "2023-01-22T12:39:49.273779Z", + "shell.execute_reply": "2023-01-22T12:39:49.272870Z", + "shell.execute_reply.started": "2023-01-22T12:39:49.263906Z" + } + }, + "outputs": [], + "source": [ + "# функция для нахождения отрицательных item\n", + "\n", + "# очень долго работает \n", + "def get_negative_sample_old(pos_i, uid_i, fts, iids_, user_interactions):\n", + " \n", + " # айтемы , с которыми взаимодействовал юзер, их исключим\n", + " user_watched_items = user_interactions[uid_i]\n", + " \n", + " # векторы айтмов, которые не смотрел юзер, и по которым посчитаем евклидовы дистанции,\n", + " # чтобы найти самые непохожие на тот айтем, который юзер смотрел\n", + "\n", + " # из всего списка item вычитаем те, с которыми пользователь взаимодействовал\n", + " # список item которых пользователь не видел\n", + " inters = np.setdiff1d(iids_, user_watched_items, assume_unique=True)\n", + " \n", + " fts_ = fts.loc[inters].sample(n = 100)\n", + " \n", + " # вектор позитивного айтема \n", + " pos_item_fts = pd.DataFrame(fts.loc[pos_i, :]).T\n", + " \n", + " # считаем дистанции\n", + " dists = ED(fts_, pos_item_fts)\n", + " \n", + " # берем десять самых непохожих и непросмотренных юзером айтемов и из них случайно выбираем один \n", + " fts_['dists'] = dists\n", + " fts_ = fts_[['dists']]\n", + " neg_candidates = fts_.sort_values(by = \"dists\")[-10:].index\n", + " \n", + " neg_i = np.random.choice(neg_candidates)\n", + " \n", + " return neg_i" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:03.755386Z", + "start_time": "2021-10-28T18:41:03.750664Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:37:53.122995Z", + "iopub.status.busy": "2023-01-22T15:37:53.122612Z", + "iopub.status.idle": "2023-01-22T15:37:53.132222Z", + "shell.execute_reply": "2023-01-22T15:37:53.130866Z", + "shell.execute_reply.started": "2023-01-22T15:37:53.122960Z" + }, + "id": "7829878b" + }, + "outputs": [], + "source": [ + "def generator(items, users, interactions, batch_size=1024):\n", + " while True:\n", + " uid_meta = []\n", + " uid_interaction = []\n", + " pos = []\n", + " neg = []\n", + " for _ in range(batch_size):\n", + " # берем рандомный uid\n", + " uid_i = randint(0, interactions.shape[0]-1)\n", + " # id хорошего айтема\n", + " pos_i = np.random.choice(range(interactions.shape[1]), p=interactions[uid_i])\n", + " # id плохого айтема\n", + " #neg_i = np.random.choice(range(interactions.shape[1]))\n", + " #neg_i = get_negative_sample_old(pos_i, uid_i, fts, iids_, user_interactions)\n", + " neg_i = get_negative_sample(pos_i, uid_i, distance_dict)\n", + " # фичи юзера\n", + " uid_meta.append(users.iloc[uid_i])\n", + " # вектор айтемов, с которыми юзер взаимодействовал\n", + " uid_interaction.append(interactions_vec[uid_i])\n", + " # фичи хорошего айтема\n", + " pos.append(items.iloc[pos_i])\n", + " # фичи плохого айтема\n", + " neg.append(items.iloc[neg_i])\n", + " \n", + " yield [np.array(uid_meta), np.array(uid_interaction), np.array(pos), np.array(neg)], [np.array(uid_meta), np.array(uid_interaction)]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.386864Z", + "start_time": "2021-10-28T18:41:03.756363Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:37:57.807501Z", + "iopub.status.busy": "2023-01-22T15:37:57.807136Z", + "iopub.status.idle": "2023-01-22T15:38:48.900316Z", + "shell.execute_reply": "2023-01-22T15:38:48.899211Z", + "shell.execute_reply.started": "2023-01-22T15:37:57.807471Z" + }, + "id": "af9d3c3b", + "outputId": "1040f567-f64a-4ccb-91a8-48034694dfdc" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "вектор фичей юзера: (1024, 19)\n", + "вектор взаимодействий юзера с айтемами: (1024, 6897)\n", + "вектор 'хорошего' айтема: (1024, 9196)\n", + "вектор 'плохого' айтема: (1024, 9196)\n", + "\n", + "вектор фичей юзера: (1024, 19)\n", + "вектор взаимодействий юзера с айтемами: (1024, 6897)\n" + ] + } + ], + "source": [ + "# инициализируем генератор\n", + "gen = generator(items=items_ohe_df.drop([\"item_id\"], axis=1), \n", + " users=users_ohe_df.drop([\"user_id\"], axis=1), \n", + " interactions=interactions_vec, batch_size=1024)\n", + "\n", + "ret = next(gen)\n", + "\n", + "\n", + "print(f\"вектор фичей юзера: {ret[0][0].shape}\")\n", + "print(f\"вектор взаимодействий юзера с айтемами: {ret[0][1].shape}\")\n", + "print(f\"вектор 'хорошего' айтема: {ret[0][2].shape}\")\n", + "print(f\"вектор 'плохого' айтема: {ret[0][3].shape}\")\n", + "print()\n", + "print(f\"вектор фичей юзера: {ret[1][0].shape}\")\n", + "print(f\"вектор взаимодействий юзера с айтемами: {ret[1][1].shape}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8bcc3e80" + }, + "source": [ + "##Генаратор, который будет использовать информацию о качестве взаимодействия юзеров с айтемами для более репрезентативного сэмплирования\n" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.493030Z", + "start_time": "2021-10-28T18:41:16.388592Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:48.903047Z", + "iopub.status.busy": "2023-01-22T15:38:48.902586Z", + "iopub.status.idle": "2023-01-22T15:38:49.025937Z", + "shell.execute_reply": "2023-01-22T15:38:49.024831Z", + "shell.execute_reply.started": "2023-01-22T15:38:48.902992Z" + }, + "id": "967b819f", + "outputId": "2f7a5885-dcb3-4ab8-80f8-57a21635595d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "N_FACTORS: 128\n", + "ITEM_MODEL_SHAPE: (9196,)\n", + "USER_META_MODEL_SHAPE: (19,)\n", + "USER_INTERACTION_MODEL_SHAPE: (6897,)\n" + ] + } + ], + "source": [ + "N_FACTORS = 128\n", + "\n", + "# в датасетах есть столбец user_id/item_id, помним, что он не является фичей для обучения!\n", + "ITEM_MODEL_SHAPE = (items_ohe_df.drop([\"item_id\"], axis=1).shape[1], ) \n", + "USER_META_MODEL_SHAPE = (users_ohe_df.drop([\"user_id\"], axis=1).shape[1], )\n", + "\n", + "USER_INTERACTION_MODEL_SHAPE = (interactions_vec.shape[1], )\n", + "\n", + "print(f\"N_FACTORS: {N_FACTORS}\")\n", + "print(f\"ITEM_MODEL_SHAPE: {ITEM_MODEL_SHAPE}\")\n", + "print(f\"USER_META_MODEL_SHAPE: {USER_META_MODEL_SHAPE}\")\n", + "print(f\"USER_INTERACTION_MODEL_SHAPE: {USER_INTERACTION_MODEL_SHAPE}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.816499Z", + "start_time": "2021-10-28T18:41:16.494387Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:49.027755Z", + "iopub.status.busy": "2023-01-22T15:38:49.027467Z", + "iopub.status.idle": "2023-01-22T15:38:53.151538Z", + "shell.execute_reply": "2023-01-22T15:38:53.150595Z", + "shell.execute_reply.started": "2023-01-22T15:38:49.027729Z" + }, + "id": "de649a01" + }, + "outputs": [], + "source": [ + "def item_model(n_factors=N_FACTORS):\n", + " # входной слой\n", + " inp = keras.layers.Input(shape=ITEM_MODEL_SHAPE)\n", + " \n", + " # полносвязный слой\n", + " layer_1 = keras.layers.Dense(N_FACTORS, activation='elu', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(inp)\n", + "\n", + " # делаем residual connection - складываем два слоя, \n", + " # чтобы градиенты не затухали во время обучения\n", + " layer_2 = keras.layers.Dense(N_FACTORS, activation='elu', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(layer_1)\n", + " \n", + " add = keras.layers.Add()([layer_1, layer_2])\n", + " \n", + " # выходной слой\n", + " out = keras.layers.Dense(N_FACTORS, activation='linear', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(add)\n", + " \n", + " return keras.models.Model(inp, out)\n", + "\n", + "\n", + "def user_model(n_factors=N_FACTORS):\n", + " # входной слой для вектора фичей юзера (из users_ohe_df)\n", + " inp_meta = keras.layers.Input(shape=USER_META_MODEL_SHAPE)\n", + " # входной слой для вектора просмотров (из iteractions_vec)\n", + " inp_interaction = keras.layers.Input(shape=USER_INTERACTION_MODEL_SHAPE)\n", + "\n", + " # полносвязный слой\n", + " layer_1_meta = keras.layers.Dense(N_FACTORS, activation='elu', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(inp_meta)\n", + "\n", + " layer_1_interaction = keras.layers.Dense(N_FACTORS, activation='elu', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(inp_interaction)\n", + "\n", + " # делаем residual connection - складываем два слоя,\n", + " # чтобы градиенты не затухали во время обучения\n", + " layer_2_meta = keras.layers.Dense(N_FACTORS, activation='elu', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(layer_1_meta)\n", + " \n", + "\n", + " add = keras.layers.Add()([layer_1_meta, layer_2_meta])\n", + " \n", + " # конкатенируем вектор фичей с вектором просмотров\n", + " concat_meta_interaction = keras.layers.Concatenate()([add, layer_1_interaction])\n", + " \n", + " # выходной слой\n", + " out = keras.layers.Dense(N_FACTORS, activation='linear', use_bias=False,\n", + " kernel_regularizer=keras.regularizers.l2(1e-6),\n", + " activity_regularizer=keras.regularizers.l2(l2=1e-6))(concat_meta_interaction)\n", + " \n", + " return keras.models.Model([inp_meta, inp_interaction], out)\n", + "\n", + "# инициализируем модели юзера и айтема\n", + "i2v = item_model()\n", + "u2v = user_model()\n", + "\n", + "# вход для вектора фичей юзера (из users_ohe_df)\n", + "ancor_meta_in = keras.layers.Input(shape=USER_META_MODEL_SHAPE)\n", + "# вход для вектора просмотра юзера (из interactions_vec)\n", + "ancor_interaction_in = keras.layers.Input(shape=USER_INTERACTION_MODEL_SHAPE)\n", + "\n", + "# вход для вектора \"хорошего\" айтема\n", + "pos_in = keras.layers.Input(shape=ITEM_MODEL_SHAPE)\n", + "# вход для вектора \"плохого\" айтема\n", + "neg_in = keras.layers.Input(shape=ITEM_MODEL_SHAPE)\n", + "\n", + "# получаем вектор юзера\n", + "ancor = u2v([ancor_meta_in, ancor_interaction_in])\n", + "# получаем вектор \"хорошего\" айтема\n", + "pos = i2v(pos_in)\n", + "# получаем вектор \"плохого\" айтема\n", + "neg = i2v(neg_in)\n", + "\n", + "# конкатенируем полученные векторы\n", + "res = keras.layers.Concatenate(name=\"concat_ancor_pos_neg\")([ancor, pos, neg])\n", + "\n", + "# собираем модель\n", + "model = keras.models.Model([ancor_meta_in, ancor_interaction_in, pos_in, neg_in], res)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.822662Z", + "start_time": "2021-10-28T18:41:16.817857Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:53.154784Z", + "iopub.status.busy": "2023-01-22T15:38:53.154419Z", + "iopub.status.idle": "2023-01-22T15:38:53.789679Z", + "shell.execute_reply": "2023-01-22T15:38:53.788675Z", + "shell.execute_reply.started": "2023-01-22T15:38:53.154748Z" + }, + "id": "e912d920" + }, + "outputs": [], + "source": [ + "model_name = 'recsys_resnet_linear'\n", + "\n", + "# логируем процесс обучения в тензорборд\n", + "t_board = keras.callbacks.TensorBoard(log_dir=f'runs/{model_name}')\n", + "\n", + "# уменьшаем learning_rate, если лосс долго не уменьшается (в течение двух эпох)\n", + "decay = keras.callbacks.ReduceLROnPlateau(monitor='loss', patience=2, factor=0.8, verbose=1)\n", + "\n", + "# сохраняем модель после каждой эпохи, если лосс уменьшился\n", + "check = keras.callbacks.ModelCheckpoint(filepath=model_name + '/epoch{epoch}-{loss:.2f}.h5', monitor=\"loss\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.832365Z", + "start_time": "2021-10-28T18:41:16.824484Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:53.792105Z", + "iopub.status.busy": "2023-01-22T15:38:53.791371Z", + "iopub.status.idle": "2023-01-22T15:38:53.808624Z", + "shell.execute_reply": "2023-01-22T15:38:53.807732Z", + "shell.execute_reply.started": "2023-01-22T15:38:53.792041Z" + }, + "id": "f95049f6" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:absl:At this time, the v2.11+ optimizer `tf.keras.optimizers.Adam` runs slowly on M1/M2 Macs, please use the legacy Keras optimizer instead, located at `tf.keras.optimizers.legacy.Adam`.\n", + "WARNING:absl:`lr` is deprecated in Keras optimizer, please use `learning_rate` or use the legacy optimizer, e.g.,tf.keras.optimizers.legacy.Adam.\n" + ] + } + ], + "source": [ + "# компилируем модель, используем оптимайзер Adam и triplet loss\n", + "opt = keras.optimizers.Adam(lr=0.001)\n", + "model.compile(loss=triplet_loss, optimizer=opt)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.867472Z", + "start_time": "2021-10-28T18:41:16.833753Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:53.811821Z", + "iopub.status.busy": "2023-01-22T15:38:53.811155Z", + "iopub.status.idle": "2023-01-22T15:38:53.852098Z", + "shell.execute_reply": "2023-01-22T15:38:53.851090Z", + "shell.execute_reply.started": "2023-01-22T15:38:53.811786Z" + }, + "id": "fb9382d0", + "outputId": "2eca9a17-1544-4e27-a483-b86d11391767" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: \"model_3\"\n", + "__________________________________________________________________________________________________\n", + " Layer (type) Output Shape Param # Connected to \n", + "==================================================================================================\n", + " input_8 (InputLayer) [(None, 9196)] 0 [] \n", + " \n", + " dense_7 (Dense) (None, 128) 1177088 ['input_8[0][0]'] \n", + " \n", + " dense_8 (Dense) (None, 128) 16384 ['dense_7[0][0]'] \n", + " \n", + " add_2 (Add) (None, 128) 0 ['dense_7[0][0]', \n", + " 'dense_8[0][0]'] \n", + " \n", + " dense_9 (Dense) (None, 128) 16384 ['add_2[0][0]'] \n", + " \n", + "==================================================================================================\n", + "Total params: 1209856 (4.62 MB)\n", + "Trainable params: 1209856 (4.62 MB)\n", + "Non-trainable params: 0 (0.00 Byte)\n", + "__________________________________________________________________________________________________\n" + ] + } + ], + "source": [ + "# модель айтема\n", + "item_model().summary()" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.923402Z", + "start_time": "2021-10-28T18:41:16.868877Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:53.854198Z", + "iopub.status.busy": "2023-01-22T15:38:53.853594Z", + "iopub.status.idle": "2023-01-22T15:38:53.908177Z", + "shell.execute_reply": "2023-01-22T15:38:53.907222Z", + "shell.execute_reply.started": "2023-01-22T15:38:53.854161Z" + }, + "id": "286149d1", + "outputId": "4284ba09-05ef-4963-c637-67e919701d19" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: \"model_4\"\n", + "__________________________________________________________________________________________________\n", + " Layer (type) Output Shape Param # Connected to \n", + "==================================================================================================\n", + " input_9 (InputLayer) [(None, 19)] 0 [] \n", + " \n", + " dense_10 (Dense) (None, 128) 2432 ['input_9[0][0]'] \n", + " \n", + " dense_12 (Dense) (None, 128) 16384 ['dense_10[0][0]'] \n", + " \n", + " input_10 (InputLayer) [(None, 6897)] 0 [] \n", + " \n", + " add_3 (Add) (None, 128) 0 ['dense_10[0][0]', \n", + " 'dense_12[0][0]'] \n", + " \n", + " dense_11 (Dense) (None, 128) 882816 ['input_10[0][0]'] \n", + " \n", + " concatenate_1 (Concatenate (None, 256) 0 ['add_3[0][0]', \n", + " ) 'dense_11[0][0]'] \n", + " \n", + " dense_13 (Dense) (None, 128) 32768 ['concatenate_1[0][0]'] \n", + " \n", + "==================================================================================================\n", + "Total params: 934400 (3.56 MB)\n", + "Trainable params: 934400 (3.56 MB)\n", + "Non-trainable params: 0 (0.00 Byte)\n", + "__________________________________________________________________________________________________\n" + ] + } + ], + "source": [ + "# модель юзера\n", + "user_model().summary()" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T18:41:16.929341Z", + "start_time": "2021-10-28T18:41:16.924663Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:53.909970Z", + "iopub.status.busy": "2023-01-22T15:38:53.909370Z", + "iopub.status.idle": "2023-01-22T15:38:53.917202Z", + "shell.execute_reply": "2023-01-22T15:38:53.916103Z", + "shell.execute_reply.started": "2023-01-22T15:38:53.909934Z" + }, + "id": "d9f25a3f", + "outputId": "6f9a3700-4420-4345-8331-82f7207b566b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: \"model_2\"\n", + "__________________________________________________________________________________________________\n", + " Layer (type) Output Shape Param # Connected to \n", + "==================================================================================================\n", + " input_4 (InputLayer) [(None, 19)] 0 [] \n", + " \n", + " input_5 (InputLayer) [(None, 6897)] 0 [] \n", + " \n", + " input_6 (InputLayer) [(None, 9196)] 0 [] \n", + " \n", + " input_7 (InputLayer) [(None, 9196)] 0 [] \n", + " \n", + " model_1 (Functional) (None, 128) 934400 ['input_4[0][0]', \n", + " 'input_5[0][0]'] \n", + " \n", + " model (Functional) (None, 128) 1209856 ['input_6[0][0]', \n", + " 'input_7[0][0]'] \n", + " \n", + " concat_ancor_pos_neg (Conc (None, 384) 0 ['model_1[0][0]', \n", + " atenate) 'model[0][0]', \n", + " 'model[1][0]'] \n", + " \n", + "==================================================================================================\n", + "Total params: 2144256 (8.18 MB)\n", + "Trainable params: 2144256 (8.18 MB)\n", + "Non-trainable params: 0 (0.00 Byte)\n", + "__________________________________________________________________________________________________\n" + ] + } + ], + "source": [ + "# общая модель\n", + "model.summary()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T19:15:21.657529Z", + "start_time": "2021-10-28T19:15:16.365923Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T15:38:53.919463Z", + "iopub.status.busy": "2023-01-22T15:38:53.918611Z", + "iopub.status.idle": "2023-01-22T16:17:01.448835Z", + "shell.execute_reply": "2023-01-22T16:17:01.447888Z", + "shell.execute_reply.started": "2023-01-22T15:38:53.919424Z" + }, + "id": "99d50830", + "outputId": "cee25813-2173-460f-e6f2-024d75d1db08" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/30\n", + "100/100 [==============================] - 42s 418ms/step - loss: 0.4123 - lr: 0.0010\n", + "Epoch 2/30\n", + "100/100 [==============================] - 42s 425ms/step - loss: 0.2973 - lr: 0.0010\n", + "Epoch 3/30\n", + "100/100 [==============================] - 42s 423ms/step - loss: 0.2713 - lr: 0.0010\n", + "Epoch 4/30\n", + "100/100 [==============================] - 642s 6s/step - loss: 0.2240 - lr: 0.0010\n", + "Epoch 5/30\n", + "100/100 [==============================] - 41s 413ms/step - loss: 0.2200 - lr: 0.0010\n", + "Epoch 6/30\n", + "100/100 [==============================] - 41s 415ms/step - loss: 0.1929 - lr: 0.0010\n", + "Epoch 7/30\n", + "100/100 [==============================] - 42s 427ms/step - loss: 0.1727 - lr: 0.0010\n", + "Epoch 8/30\n", + "100/100 [==============================] - 42s 423ms/step - loss: 0.1849 - lr: 0.0010\n", + "Epoch 9/30\n", + "100/100 [==============================] - 41s 418ms/step - loss: 0.1594 - lr: 0.0010\n", + "Epoch 10/30\n", + "100/100 [==============================] - 42s 420ms/step - loss: 0.1485 - lr: 0.0010\n", + "Epoch 11/30\n", + "100/100 [==============================] - 41s 417ms/step - loss: 0.1523 - lr: 0.0010\n", + "Epoch 12/30\n", + "100/100 [==============================] - 41s 415ms/step - loss: 0.1328 - lr: 0.0010\n", + "Epoch 13/30\n", + "100/100 [==============================] - 41s 419ms/step - loss: 0.1407 - lr: 0.0010\n", + "Epoch 14/30\n", + "100/100 [==============================] - ETA: 0s - loss: 0.1524\n", + "Epoch 14: ReduceLROnPlateau reducing learning rate to 0.000800000037997961.\n", + "100/100 [==============================] - 42s 421ms/step - loss: 0.1524 - lr: 0.0010\n", + "Epoch 15/30\n", + "100/100 [==============================] - 42s 420ms/step - loss: 0.1304 - lr: 8.0000e-04\n", + "Epoch 16/30\n", + "100/100 [==============================] - 42s 421ms/step - loss: 0.1305 - lr: 8.0000e-04\n", + "Epoch 17/30\n", + "100/100 [==============================] - 41s 415ms/step - loss: 0.1299 - lr: 8.0000e-04\n", + "Epoch 18/30\n", + "100/100 [==============================] - 41s 416ms/step - loss: 0.1332 - lr: 8.0000e-04\n", + "Epoch 19/30\n", + "100/100 [==============================] - 578s 6s/step - loss: 0.1128 - lr: 8.0000e-04\n", + "Epoch 20/30\n", + "100/100 [==============================] - 298s 3s/step - loss: 0.1168 - lr: 8.0000e-04\n", + "Epoch 21/30\n", + "100/100 [==============================] - ETA: 0s - loss: 0.1145\n", + "Epoch 21: ReduceLROnPlateau reducing learning rate to 0.0006400000303983689.\n", + "100/100 [==============================] - 42s 424ms/step - loss: 0.1145 - lr: 8.0000e-04\n", + "Epoch 22/30\n", + "100/100 [==============================] - 42s 423ms/step - loss: 0.1230 - lr: 6.4000e-04\n", + "Epoch 23/30\n", + "100/100 [==============================] - 42s 421ms/step - loss: 0.1108 - lr: 6.4000e-04\n", + "Epoch 24/30\n", + "100/100 [==============================] - 42s 422ms/step - loss: 0.0976 - lr: 6.4000e-04\n", + "Epoch 25/30\n", + "100/100 [==============================] - 42s 422ms/step - loss: 0.1116 - lr: 6.4000e-04\n", + "Epoch 26/30\n", + "100/100 [==============================] - ETA: 0s - loss: 0.0996\n", + "Epoch 26: ReduceLROnPlateau reducing learning rate to 0.0005120000336319208.\n", + "100/100 [==============================] - 43s 430ms/step - loss: 0.0996 - lr: 6.4000e-04\n", + "Epoch 27/30\n", + "100/100 [==============================] - 43s 433ms/step - loss: 0.1010 - lr: 5.1200e-04\n", + "Epoch 28/30\n", + "100/100 [==============================] - ETA: 0s - loss: 0.0984\n", + "Epoch 28: ReduceLROnPlateau reducing learning rate to 0.00040960004553198815.\n", + "100/100 [==============================] - 42s 429ms/step - loss: 0.0984 - lr: 5.1200e-04\n", + "Epoch 29/30\n", + "100/100 [==============================] - 43s 430ms/step - loss: 0.0865 - lr: 4.0960e-04\n", + "Epoch 30/30\n", + "100/100 [==============================] - 43s 433ms/step - loss: 0.1049 - lr: 4.0960e-04\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# начинаем обучение, не забывая дропнуть столбцы item_id и user_id \n", + "# из датафреймов при инициализации генератора.\n", + "\n", + "# batch_size можно (и лучше) поставить побольше, если вы не органичены в ресурсах\n", + "\n", + "model.fit(generator(items=items_ohe_df.drop([\"item_id\"], axis=1), \n", + " users=users_ohe_df.drop([\"user_id\"], axis=1), \n", + " interactions=interactions_vec,\n", + " batch_size=16), \n", + " steps_per_epoch=100, \n", + " epochs=30, \n", + " initial_epoch=0,\n", + " callbacks=[decay, t_board, check]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:17:01.453483Z", + "iopub.status.busy": "2023-01-22T16:17:01.453198Z", + "iopub.status.idle": "2023-01-22T16:17:01.486783Z", + "shell.execute_reply": "2023-01-22T16:17:01.485812Z", + "shell.execute_reply.started": "2023-01-22T16:17:01.453458Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.\n" + ] + } + ], + "source": [ + "i2v.save('i2v.hdf5')\n", + "u2v.save('u2v.hdf5')" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T19:15:26.511958Z", + "start_time": "2021-10-28T19:15:26.151899Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:24:28.685695Z", + "iopub.status.busy": "2023-01-22T16:24:28.685290Z", + "iopub.status.idle": "2023-01-22T16:24:30.854186Z", + "shell.execute_reply": "2023-01-22T16:24:30.853120Z", + "shell.execute_reply.started": "2023-01-22T16:24:28.685657Z" + }, + "id": "94d23f62", + "outputId": "4a500ea7-fa38-4455-a51a-2bd0113fa2f2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1/1 [==============================] - 0s 129ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[0.76927984]], dtype=float32)" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# берем рандомного юзера\n", + "rand_uid = np.random.choice(list(users_ohe_df.index))\n", + "\n", + "# получаем фичи юзера и вектор его просмотров айтемов\n", + "user_meta_feats = users_ohe_df.drop([\"user_id\"], axis=1).iloc[rand_uid]\n", + "user_interaction_vec = interactions_vec[rand_uid]\n", + "\n", + "# берем рандомный айтем\n", + "rand_iid = np.random.choice(list(items_ohe_df.index))\n", + "# получаем фичи айтема\n", + "item_feats = items_ohe_df.drop([\"item_id\"], axis=1).iloc[rand_iid]\n", + "\n", + "# получаем вектор юзера\n", + "user_vec = u2v.predict([np.array(user_meta_feats).reshape(1, -1), \n", + " np.array(user_interaction_vec).reshape(1, -1)])\n", + "\n", + "# и вектор айтема\n", + "item_vec = i2v.predict(np.array(item_feats).reshape(1, -1))\n", + "\n", + "# считаем расстояние между вектором юзера и вектором айтема\n", + "from sklearn.metrics.pairwise import euclidean_distances as ED\n", + "\n", + "ED(user_vec, item_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "ExecuteTime": { + "end_time": "2021-10-28T19:15:28.951471Z", + "start_time": "2021-10-28T19:15:27.763367Z" + }, + "execution": { + "iopub.execute_input": "2023-01-22T16:24:35.398767Z", + "iopub.status.busy": "2023-01-22T16:24:35.398342Z", + "iopub.status.idle": "2023-01-22T16:24:37.179114Z", + "shell.execute_reply": "2023-01-22T16:24:37.177336Z", + "shell.execute_reply.started": "2023-01-22T16:24:35.398731Z" + }, + "id": "d537d3e8", + "outputId": "6bdce370-c348-4f0b-cd4f-3cbb0ec3d019" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "216/216 [==============================] - 0s 883us/step\n" + ] + } + ], + "source": [ + "# получаем фичи всех айтемов\n", + "items_feats = items_ohe_df.drop([\"item_id\"], axis=1).to_numpy()\n", + "# получаем векторы всех айтемов\n", + "items_vecs = i2v.predict(items_feats)\n", + "\n", + "# считаем расстояния\n", + "dists = ED(user_vec, items_vecs)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:37.200481Z", + "iopub.status.busy": "2023-01-22T16:24:37.199790Z", + "iopub.status.idle": "2023-01-22T16:24:37.219685Z", + "shell.execute_reply": "2023-01-22T16:24:37.218365Z", + "shell.execute_reply.started": "2023-01-22T16:24:37.200421Z" + }, + "id": "udY36b_l0okL", + "outputId": "53287102-2434-490e-d668-b8085515d4b8" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(6897, 128)" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items_vecs.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:38.051890Z", + "iopub.status.busy": "2023-01-22T16:24:38.051199Z", + "iopub.status.idle": "2023-01-22T16:24:38.063043Z", + "shell.execute_reply": "2023-01-22T16:24:38.061416Z", + "shell.execute_reply.started": "2023-01-22T16:24:38.051840Z" + }, + "id": "XasFl6RN0snT" + }, + "outputs": [], + "source": [ + "users_meta_feats = users_ohe_df.drop([\"user_id\"], axis=1)\n", + "users_interaction_vec = interactions_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:38.561257Z", + "iopub.status.busy": "2023-01-22T16:24:38.560146Z", + "iopub.status.idle": "2023-01-22T16:24:38.568144Z", + "shell.execute_reply": "2023-01-22T16:24:38.566777Z", + "shell.execute_reply.started": "2023-01-22T16:24:38.561176Z" + }, + "id": "cntEZU450_MI", + "outputId": "c9aace32-281a-4b0e-8e0d-8b0f1088ce9b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 19)" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users_meta_feats.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:40.475433Z", + "iopub.status.busy": "2023-01-22T16:24:40.472691Z", + "iopub.status.idle": "2023-01-22T16:24:40.484559Z", + "shell.execute_reply": "2023-01-22T16:24:40.483559Z", + "shell.execute_reply.started": "2023-01-22T16:24:40.475392Z" + }, + "id": "kQ1EZolS1B1Y", + "outputId": "ca9dc5eb-5519-4c75-f1d8-4945941a46d1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 6897)" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users_interaction_vec.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:40.786186Z", + "iopub.status.busy": "2023-01-22T16:24:40.785775Z", + "iopub.status.idle": "2023-01-22T16:24:40.797826Z", + "shell.execute_reply": "2023-01-22T16:24:40.796580Z", + "shell.execute_reply.started": "2023-01-22T16:24:40.786151Z" + }, + "id": "hKU4MD7M1dp5", + "outputId": "bd79e8e2-8a82-4ff7-d1ac-849e3425c2c8" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 19)" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array(users_meta_feats).shape" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:41.775332Z", + "iopub.status.busy": "2023-01-22T16:24:41.774265Z", + "iopub.status.idle": "2023-01-22T16:24:41.780836Z", + "shell.execute_reply": "2023-01-22T16:24:41.779665Z", + "shell.execute_reply.started": "2023-01-22T16:24:41.775281Z" + } + }, + "outputs": [], + "source": [ + "del interactions_vec\n", + "del users_df, interactions_df" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:24:52.966474Z", + "iopub.status.busy": "2023-01-22T16:24:52.965002Z", + "iopub.status.idle": "2023-01-22T16:24:57.402446Z", + "shell.execute_reply": "2023-01-22T16:24:57.401009Z", + "shell.execute_reply.started": "2023-01-22T16:24:52.966417Z" + }, + "id": "x16g5FM21XGJ", + "outputId": "9b43e3e6-f98b-466a-8b0d-c2aeb0ca724e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "625/625 [==============================] - 1s 809us/step\n", + "625/625 [==============================] - 0s 779us/step\n", + "812/812 [==============================] - 1s 763us/step\n" + ] + } + ], + "source": [ + "users_vec_1 = u2v.predict([np.array(users_meta_feats.iloc[:20000]), \n", + " np.array(users_interaction_vec[:20000])])\n", + "users_vec_2 = u2v.predict([np.array(users_meta_feats.iloc[20000:40000]), \n", + " np.array(users_interaction_vec[20000:40000])])\n", + "users_vec_3 = u2v.predict([np.array(users_meta_feats.iloc[40000:]), \n", + " np.array(users_interaction_vec[40000:])])\n", + "users_vec = np.concatenate((users_vec_1, users_vec_2, users_vec_3))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:25:54.668894Z", + "iopub.status.busy": "2023-01-22T16:25:54.667629Z", + "iopub.status.idle": "2023-01-22T16:25:54.674606Z", + "shell.execute_reply": "2023-01-22T16:25:54.673447Z", + "shell.execute_reply.started": "2023-01-22T16:25:54.668856Z" + } + }, + "outputs": [], + "source": [ + "del users_vec_1, users_vec_2, users_vec_3, users_interaction_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:25:57.451831Z", + "iopub.status.busy": "2023-01-22T16:25:57.451189Z", + "iopub.status.idle": "2023-01-22T16:25:57.458982Z", + "shell.execute_reply": "2023-01-22T16:25:57.457745Z", + "shell.execute_reply.started": "2023-01-22T16:25:57.451795Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 128)" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users_vec.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:20:21.497209Z", + "iopub.status.busy": "2023-01-22T16:20:21.496765Z", + "iopub.status.idle": "2023-01-22T16:20:21.504388Z", + "shell.execute_reply": "2023-01-22T16:20:21.503250Z", + "shell.execute_reply.started": "2023-01-22T16:20:21.497158Z" + }, + "id": "G4pntPu10ogl", + "outputId": "557b6f56-dff5-46e9-da97-569001c59c79" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(6897, 128)" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items_vecs.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:26:12.212846Z", + "iopub.status.busy": "2023-01-22T16:26:12.212440Z", + "iopub.status.idle": "2023-01-22T16:26:19.980077Z", + "shell.execute_reply": "2023-01-22T16:26:19.978704Z", + "shell.execute_reply.started": "2023-01-22T16:26:12.212812Z" + }, + "id": "hnUX3Yte2Jcw" + }, + "outputs": [], + "source": [ + "dists = ED(users_vec, items_vecs)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:26:33.221953Z", + "iopub.status.busy": "2023-01-22T16:26:33.220783Z", + "iopub.status.idle": "2023-01-22T16:26:33.231255Z", + "shell.execute_reply": "2023-01-22T16:26:33.229877Z", + "shell.execute_reply.started": "2023-01-22T16:26:33.221902Z" + }, + "id": "MDgiwnnu2KHk", + "outputId": "ae9eeb6f-8a29-4195-8bdf-eeaa51b6d049" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 6897)" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dists.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:26:36.257959Z", + "iopub.status.busy": "2023-01-22T16:26:36.257347Z", + "iopub.status.idle": "2023-01-22T16:26:45.531254Z", + "shell.execute_reply": "2023-01-22T16:26:45.530120Z", + "shell.execute_reply.started": "2023-01-22T16:26:36.257910Z" + }, + "id": "Ru8IQwSV2UrB" + }, + "outputs": [], + "source": [ + "top10_iids_1 = np.argsort(dists[:20000], axis=1)[:,:10]\n", + "top10_iids_2 = np.argsort(dists[20000:40000], axis=1)[:,:10]\n", + "top10_iids_3 = np.argsort(dists[40000:], axis=1)[:,:10]\n", + "top10_iids = np.concatenate((top10_iids_1, top10_iids_2, top10_iids_3))" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:28:25.544273Z", + "iopub.status.busy": "2023-01-22T16:28:25.543272Z", + "iopub.status.idle": "2023-01-22T16:28:25.551809Z", + "shell.execute_reply": "2023-01-22T16:28:25.550511Z", + "shell.execute_reply.started": "2023-01-22T16:28:25.544233Z" + }, + "id": "pAzg23jU3TSo", + "outputId": "baeb6ea9-ca6f-4bb9-da7b-3fc16669db23" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 10)" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top10_iids.reshape(dists.shape[0], 10).shape" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:28:37.182827Z", + "iopub.status.busy": "2023-01-22T16:28:37.182088Z", + "iopub.status.idle": "2023-01-22T16:28:37.190183Z", + "shell.execute_reply": "2023-01-22T16:28:37.188831Z", + "shell.execute_reply.started": "2023-01-22T16:28:37.182788Z" + }, + "id": "ehH1-C-S6yE9", + "outputId": "5a08578e-7bc1-404a-db00-5d2114b7ad28" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 10)" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top10_iids.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:28:47.517537Z", + "iopub.status.busy": "2023-01-22T16:28:47.516704Z", + "iopub.status.idle": "2023-01-22T16:28:47.800629Z", + "shell.execute_reply": "2023-01-22T16:28:47.799272Z", + "shell.execute_reply.started": "2023-01-22T16:28:47.517501Z" + }, + "id": "srptkYsFsk1V" + }, + "outputs": [], + "source": [ + "top10_iids_item = [iid_to_item_id[iid] for iid in top10_iids.reshape(-1)]" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:28:51.654826Z", + "iopub.status.busy": "2023-01-22T16:28:51.653959Z", + "iopub.status.idle": "2023-01-22T16:28:51.700602Z", + "shell.execute_reply": "2023-01-22T16:28:51.699239Z", + "shell.execute_reply.started": "2023-01-22T16:28:51.654791Z" + }, + "id": "GWCz9zErskwn" + }, + "outputs": [], + "source": [ + "top10_iids_item = np.array(top10_iids_item).reshape(top10_iids.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:28:57.535876Z", + "iopub.status.busy": "2023-01-22T16:28:57.535194Z", + "iopub.status.idle": "2023-01-22T16:28:57.543046Z", + "shell.execute_reply": "2023-01-22T16:28:57.541704Z", + "shell.execute_reply.started": "2023-01-22T16:28:57.535836Z" + }, + "id": "pNq_brUisknx", + "outputId": "f1980332-ef9e-4920-b470-675a567c815a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65974, 10)" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top10_iids_item.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:00.647959Z", + "iopub.status.busy": "2023-01-22T16:29:00.646906Z", + "iopub.status.idle": "2023-01-22T16:29:00.657077Z", + "shell.execute_reply": "2023-01-22T16:29:00.655386Z", + "shell.execute_reply.started": "2023-01-22T16:29:00.647919Z" + }, + "id": "z6ussvRSth2h" + }, + "outputs": [], + "source": [ + "df_dssm = pd.DataFrame(columns = ['user_id', 'item_id'])" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:08.741226Z", + "iopub.status.busy": "2023-01-22T16:29:08.740780Z", + "iopub.status.idle": "2023-01-22T16:29:08.751118Z", + "shell.execute_reply": "2023-01-22T16:29:08.750073Z", + "shell.execute_reply.started": "2023-01-22T16:29:08.741183Z" + }, + "id": "Y9XvpPzRu82h", + "outputId": "8397c843-1427-444e-af8d-f5c5cd19a80d" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [user_id, item_id]\n", + "Index: []" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_dssm.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:16.894986Z", + "iopub.status.busy": "2023-01-22T16:29:16.894575Z", + "iopub.status.idle": "2023-01-22T16:29:16.955651Z", + "shell.execute_reply": "2023-01-22T16:29:16.954612Z", + "shell.execute_reply.started": "2023-01-22T16:29:16.894935Z" + }, + "id": "KieINSdwvIu7" + }, + "outputs": [], + "source": [ + "df_dssm = pd.DataFrame({'user_id': [uid_to_user_id[uid] for uid in np.arange(top10_iids_item.shape[0])]})" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:18.345819Z", + "iopub.status.busy": "2023-01-22T16:29:18.345404Z", + "iopub.status.idle": "2023-01-22T16:29:18.371714Z", + "shell.execute_reply": "2023-01-22T16:29:18.370527Z", + "shell.execute_reply.started": "2023-01-22T16:29:18.345785Z" + }, + "id": "RSYHUj7IuzT1" + }, + "outputs": [], + "source": [ + "df_dssm['item_id'] = list(top10_iids_item)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:19.355843Z", + "iopub.status.busy": "2023-01-22T16:29:19.355038Z", + "iopub.status.idle": "2023-01-22T16:29:20.100815Z", + "shell.execute_reply": "2023-01-22T16:29:20.099612Z", + "shell.execute_reply.started": "2023-01-22T16:29:19.355801Z" + }, + "id": "xdPs4HY874OZ" + }, + "outputs": [], + "source": [ + "df_dssm = df_dssm.explode('item_id')\n", + "df_dssm['rank'] = df_dssm.groupby('user_id').cumcount() + 1\n", + "df_dssm = df_dssm.groupby('user_id').agg({'item_id': list}).reset_index()" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:20.104964Z", + "iopub.status.busy": "2023-01-22T16:29:20.104605Z", + "iopub.status.idle": "2023-01-22T16:29:20.117444Z", + "shell.execute_reply": "2023-01-22T16:29:20.115641Z", + "shell.execute_reply.started": "2023-01-22T16:29:20.104915Z" + }, + "id": "C8kdzzf6wAuz", + "outputId": "df930923-8ad5-45f8-f76c-ab847237802d" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_id
02[4457, 4151, 142, 9988, 4475, 4740, 9169, 5982...
121[4457, 3734, 9988, 4740, 2954, 2657, 4151, 152...
253[4457, 2220, 4151, 142, 4740, 15297, 2657, 134...
360[4457, 4151, 142, 9988, 3734, 6443, 4740, 2954...
481[4151, 4740, 2657, 4457, 15297, 281, 142, 9169...
\n", + "
" + ], + "text/plain": [ + " user_id item_id\n", + "0 2 [4457, 4151, 142, 9988, 4475, 4740, 9169, 5982...\n", + "1 21 [4457, 3734, 9988, 4740, 2954, 2657, 4151, 152...\n", + "2 53 [4457, 2220, 4151, 142, 4740, 15297, 2657, 134...\n", + "3 60 [4457, 4151, 142, 9988, 3734, 6443, 4740, 2954...\n", + "4 81 [4151, 4740, 2657, 4457, 15297, 281, 142, 9169..." + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_dssm.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "execution": { + "iopub.execute_input": "2023-01-22T16:29:31.350415Z", + "iopub.status.busy": "2023-01-22T16:29:31.349997Z", + "iopub.status.idle": "2023-01-22T16:29:31.715454Z", + "shell.execute_reply": "2023-01-22T16:29:31.714324Z", + "shell.execute_reply.started": "2023-01-22T16:29:31.350382Z" + } + }, + "outputs": [], + "source": [ + "df_dssm.to_csv('dssm_predictions.csv', index = False)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/hw_5_recbool.ipynb b/hw_5_recbool.ipynb new file mode 100644 index 00000000..cf414323 --- /dev/null +++ b/hw_5_recbool.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"code","execution_count":18,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T17:56:37.034499Z","iopub.status.busy":"2023-01-22T17:56:37.034012Z","iopub.status.idle":"2023-01-22T17:56:37.042666Z","shell.execute_reply":"2023-01-22T17:56:37.041481Z","shell.execute_reply.started":"2023-01-22T17:56:37.034455Z"},"papermill":{"duration":1.244043,"end_time":"2022-11-27T16:33:29.277270","exception":false,"start_time":"2022-11-27T16:33:28.033227","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["import ast\n","import json\n","import matplotlib.pyplot as plt\n","import numpy as np\n","import os\n","import pandas as pd\n","import pickle\n","\n","import warnings\n","warnings.filterwarnings('ignore')\n","\n","from collections import Counter\n","from random import randint, random\n","from scipy.sparse import coo_matrix, hstack\n","from sklearn.metrics.pairwise import euclidean_distances, cosine_distances, cosine_similarity"]},{"cell_type":"code","execution_count":20,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:02:33.523160Z","iopub.status.busy":"2023-01-22T18:02:33.522766Z","iopub.status.idle":"2023-01-22T18:02:36.724444Z","shell.execute_reply":"2023-01-22T18:02:36.723409Z","shell.execute_reply.started":"2023-01-22T18:02:33.523126Z"},"papermill":{"duration":6.445298,"end_time":"2022-11-27T16:33:35.747539","exception":false,"start_time":"2022-11-27T16:33:29.302241","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["interactions_df = pd.read_csv('interactions_processed_kion.csv')\n","users_df = pd.read_csv('users_processed_kion.csv')\n","items_df = pd.read_csv('items_processed_kion.csv')"]},{"cell_type":"code","execution_count":21,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:02:41.118088Z","iopub.status.busy":"2023-01-22T18:02:41.117711Z","iopub.status.idle":"2023-01-22T18:02:42.100146Z","shell.execute_reply":"2023-01-22T18:02:42.098848Z","shell.execute_reply.started":"2023-01-22T18:02:41.118057Z"},"papermill":{"duration":0.925082,"end_time":"2022-11-27T16:33:36.677439","exception":false,"start_time":"2022-11-27T16:33:35.752357","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["interactions_df['t_dat'] = pd.to_datetime(interactions_df['last_watch_dt'], format=\"%Y-%m-%d\")\n","interactions_df['timestamp'] = interactions_df.t_dat.values.astype(np.int64) // 10 ** 9"]},{"cell_type":"code","execution_count":22,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:02:42.111635Z","iopub.status.busy":"2023-01-22T18:02:42.110287Z","iopub.status.idle":"2023-01-22T18:02:42.408437Z","shell.execute_reply":"2023-01-22T18:02:42.407310Z","shell.execute_reply.started":"2023-01-22T18:02:42.111593Z"},"papermill":{"duration":0.284147,"end_time":"2022-11-27T16:33:36.966533","exception":false,"start_time":"2022-11-27T16:33:36.682386","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["df = interactions_df[['user_id', 'item_id', 'timestamp']].rename(\n"," columns={'user_id': 'user_id:token', 'item_id': 'item_id:token', 'timestamp': 'timestamp:float'})"]},{"cell_type":"code","execution_count":23,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:02:43.902049Z","iopub.status.busy":"2023-01-22T18:02:43.901227Z","iopub.status.idle":"2023-01-22T18:02:43.927071Z","shell.execute_reply":"2023-01-22T18:02:43.925875Z","shell.execute_reply.started":"2023-01-22T18:02:43.902007Z"},"trusted":true},"outputs":[{"data":{"text/html":["
\n","\n","\n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n","
user_id:tokenitem_id:tokentimestamp:float
017654995061620691200
169931716591622246400
265668371071620518400
386461376381625443200
496486895061619740800
............
5476246648596122251628812800
547624754686296731618272000
5476248697262152971629417600
5476249384202161971618790400
547625031970944361628985600
\n","

5476251 rows × 3 columns

\n","
"],"text/plain":[" user_id:token item_id:token timestamp:float\n","0 176549 9506 1620691200\n","1 699317 1659 1622246400\n","2 656683 7107 1620518400\n","3 864613 7638 1625443200\n","4 964868 9506 1619740800\n","... ... ... ...\n","5476246 648596 12225 1628812800\n","5476247 546862 9673 1618272000\n","5476248 697262 15297 1629417600\n","5476249 384202 16197 1618790400\n","5476250 319709 4436 1628985600\n","\n","[5476251 rows x 3 columns]"]},"execution_count":23,"metadata":{},"output_type":"execute_result"}],"source":["df"]},{"cell_type":"code","execution_count":25,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:02:47.909321Z","iopub.status.busy":"2023-01-22T18:02:47.908208Z","iopub.status.idle":"2023-01-22T18:02:54.589560Z","shell.execute_reply":"2023-01-22T18:02:54.588499Z","shell.execute_reply.started":"2023-01-22T18:02:47.909281Z"},"papermill":{"duration":7.834652,"end_time":"2022-11-27T16:33:45.906924","exception":false,"start_time":"2022-11-27T16:33:38.072272","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["df.to_csv('recbox_data/recbox_data.inter', index=False, sep='\\t')"]},{"cell_type":"code","execution_count":28,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:02:54.592386Z","iopub.status.busy":"2023-01-22T18:02:54.591996Z","iopub.status.idle":"2023-01-22T18:02:55.527789Z","shell.execute_reply":"2023-01-22T18:02:55.526787Z","shell.execute_reply.started":"2023-01-22T18:02:54.592332Z"},"papermill":{"duration":3.067001,"end_time":"2022-11-27T16:34:04.068318","exception":false,"start_time":"2022-11-27T16:34:01.001317","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["import logging\n","from logging import getLogger\n","from recbole.config import Config\n","from recbole.data import create_dataset, data_preparation\n","from recbole.model.sequential_recommender import GRU4Rec, Caser\n","from recbole.trainer import Trainer\n","from recbole.utils import init_seed, init_logger\n","from recbole.quick_start import run_recbole"]},{"cell_type":"code","execution_count":29,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:04:51.862473Z","iopub.status.busy":"2023-01-22T18:04:51.862041Z","iopub.status.idle":"2023-01-22T18:04:51.900690Z","shell.execute_reply":"2023-01-22T18:04:51.899741Z","shell.execute_reply.started":"2023-01-22T18:04:51.862435Z"},"papermill":{"duration":0.145622,"end_time":"2022-11-27T16:34:04.220395","exception":false,"start_time":"2022-11-27T16:34:04.074773","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["parameter_dict = {\n"," 'data_path': '',\n"," 'USER_ID_FIELD': 'user_id',\n"," 'ITEM_ID_FIELD': 'item_id',\n"," 'TIME_FIELD': 'timestamp',\n"," 'device': 'GPU',\n"," 'user_inter_num_interval': \"[40,inf)\",\n"," 'item_inter_num_interval': \"[40,inf)\",\n"," 'load_col': {'inter': ['user_id', 'item_id', 'timestamp']},\n"," 'neg_sampling': None,\n"," 'epochs': 10,\n"," 'verbose': -1,\n"," 'show_progress' : False,\n"," 'eval_args': {\n"," 'split': {'RS': [9, 0, 1]},\n"," 'group_by': 'user',\n"," 'order': 'TO',\n"," 'mode': 'full'}\n","}\n","config = Config(model='MultiVAE', dataset='recbox_data', config_dict=parameter_dict)\n","\n","# init random seed\n","init_seed(config['seed'], config['reproducibility'])\n","\n","# logger initialization\n","init_logger(config)\n","logger = getLogger()\n","# Create handlers\n","c_handler = logging.StreamHandler()\n","c_handler.setLevel(logging.INFO)\n","logger.addHandler(c_handler)\n","\n","# write config info into log\n","# logger.info(config)"]},{"cell_type":"code","execution_count":30,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:04:55.538201Z","iopub.status.busy":"2023-01-22T18:04:55.537818Z","iopub.status.idle":"2023-01-22T18:05:32.322220Z","shell.execute_reply":"2023-01-22T18:05:32.321423Z","shell.execute_reply.started":"2023-01-22T18:04:55.538170Z"},"papermill":{"duration":42.583583,"end_time":"2022-11-27T16:34:46.811041","exception":false,"start_time":"2022-11-27T16:34:04.227458","status":"completed"},"tags":[],"trusted":true},"outputs":[{"name":"stderr","output_type":"stream","text":["11 Dec 11:56 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n"]}],"source":["dataset = create_dataset(config)\n","logger.info(dataset)"]},{"cell_type":"code","execution_count":31,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:05:32.324208Z","iopub.status.busy":"2023-01-22T18:05:32.323852Z","iopub.status.idle":"2023-01-22T18:05:34.256086Z","shell.execute_reply":"2023-01-22T18:05:34.255320Z","shell.execute_reply.started":"2023-01-22T18:05:32.324171Z"},"papermill":{"duration":2.241551,"end_time":"2022-11-27T16:34:49.059852","exception":false,"start_time":"2022-11-27T16:34:46.818301","status":"completed"},"tags":[],"trusted":true},"outputs":[{"name":"stderr","output_type":"stream","text":["11 Dec 11:56 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","11 Dec 11:56 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n"]}],"source":["# dataset splitting\n","train_data, valid_data, test_data = data_preparation(config, dataset)"]},{"cell_type":"code","execution_count":32,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:05:34.257762Z","iopub.status.busy":"2023-01-22T18:05:34.257174Z","iopub.status.idle":"2023-01-22T18:05:34.262360Z","shell.execute_reply":"2023-01-22T18:05:34.261553Z","shell.execute_reply.started":"2023-01-22T18:05:34.257723Z"},"papermill":{"duration":0.01694,"end_time":"2022-11-27T16:34:49.085164","exception":false,"start_time":"2022-11-27T16:34:49.068224","status":"completed"},"tags":[],"trusted":true},"outputs":[],"source":["import time"]},{"cell_type":"markdown","metadata":{},"source":["### Использование различных архитектур"]},{"cell_type":"code","execution_count":33,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:05:41.096708Z","iopub.status.busy":"2023-01-22T18:05:41.096214Z","iopub.status.idle":"2023-01-22T18:11:38.568018Z","shell.execute_reply":"2023-01-22T18:11:38.567070Z","shell.execute_reply.started":"2023-01-22T18:05:41.096667Z"},"papermill":{"duration":27259.293886,"end_time":"2022-11-28T00:09:08.387403","exception":false,"start_time":"2022-11-27T16:34:49.093517","status":"completed"},"tags":[],"trusted":true},"outputs":[{"name":"stdout","output_type":"stream","text":["running LightGCN...\n"]},{"name":"stderr","output_type":"stream","text":["11 Dec 11:56 INFO ['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","11 Dec 11:56 INFO \n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = False\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","embedding_size = 64\n","n_layers = 2\n","reg_weight = 1e-05\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.GENERAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.PAIRWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","\n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = False\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","embedding_size = 64\n","n_layers = 2\n","reg_weight = 1e-05\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.GENERAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.PAIRWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","11 Dec 11:58 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","11 Dec 11:58 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","11 Dec 11:58 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","11 Dec 11:58 INFO LightGCN(\n"," (user_embedding): Embedding(13355, 64)\n"," (item_embedding): Embedding(3294, 64)\n"," (mf_loss): BPRLoss()\n"," (reg_loss): EmbLoss()\n",")\n","Trainable parameters: 1065536\n","LightGCN(\n"," (user_embedding): Embedding(13355, 64)\n"," (item_embedding): Embedding(3294, 64)\n"," (mf_loss): BPRLoss()\n"," (reg_loss): EmbLoss()\n",")\n","Trainable parameters: 1065536\n","11 Dec 11:58 INFO FLOPs: 0.0\n","FLOPs: 0.0\n","11 Dec 12:00 INFO epoch 0 training [time: 96.30s, train loss: 201.8552]\n","epoch 0 training [time: 96.30s, train loss: 201.8552]\n","11 Dec 12:00 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:01 INFO epoch 1 training [time: 91.67s, train loss: 166.0586]\n","epoch 1 training [time: 91.67s, train loss: 166.0586]\n","11 Dec 12:01 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:03 INFO epoch 2 training [time: 106.54s, train loss: 156.0221]\n","epoch 2 training [time: 106.54s, train loss: 156.0221]\n","11 Dec 12:03 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:05 INFO epoch 3 training [time: 125.49s, train loss: 149.5909]\n","epoch 3 training [time: 125.49s, train loss: 149.5909]\n","11 Dec 12:05 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:07 INFO epoch 4 training [time: 123.82s, train loss: 146.1851]\n","epoch 4 training [time: 123.82s, train loss: 146.1851]\n","11 Dec 12:07 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:10 INFO epoch 5 training [time: 135.29s, train loss: 143.7300]\n","epoch 5 training [time: 135.29s, train loss: 143.7300]\n","11 Dec 12:10 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:12 INFO epoch 6 training [time: 152.14s, train loss: 141.2300]\n","epoch 6 training [time: 152.14s, train loss: 141.2300]\n","11 Dec 12:12 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:14 INFO epoch 7 training [time: 114.99s, train loss: 137.4871]\n","epoch 7 training [time: 114.99s, train loss: 137.4871]\n","11 Dec 12:14 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:16 INFO epoch 8 training [time: 128.70s, train loss: 133.3195]\n","epoch 8 training [time: 128.70s, train loss: 133.3195]\n","11 Dec 12:16 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:18 INFO epoch 9 training [time: 126.52s, train loss: 129.4056]\n","epoch 9 training [time: 126.52s, train loss: 129.4056]\n","11 Dec 12:18 INFO Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Saving current: saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:18 INFO Loading model structure and parameters from saved/LightGCN-Dec-11-2023_11-58-43.pth\n","Loading model structure and parameters from saved/LightGCN-Dec-11-2023_11-58-43.pth\n","11 Dec 12:18 INFO The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 48.50 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.07 G/8.00 G |\n","+-------------+---------------+\n","The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 48.50 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.07 G/8.00 G |\n","+-------------+---------------+\n","11 Dec 12:18 INFO best valid : None\n","best valid : None\n","11 Dec 12:18 INFO test result: OrderedDict([('recall@10', 0.0792), ('mrr@10', 0.1685), ('ndcg@10', 0.0795), ('hit@10', 0.3385), ('precision@10', 0.0441)])\n","test result: OrderedDict([('recall@10', 0.0792), ('mrr@10', 0.1685), ('ndcg@10', 0.0795), ('hit@10', 0.3385), ('precision@10', 0.0441)])\n"]},{"name":"stdout","output_type":"stream","text":["It took 21.95 mins\n","{'best_valid_score': -inf, 'valid_score_bigger': True, 'best_valid_result': None, 'test_result': OrderedDict([('recall@10', 0.0792), ('mrr@10', 0.1685), ('ndcg@10', 0.0795), ('hit@10', 0.3385), ('precision@10', 0.0441)])}\n","running MultiVAE...\n"]},{"name":"stderr","output_type":"stream","text":["11 Dec 12:18 INFO ['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","11 Dec 12:18 INFO \n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = False\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","mlp_hidden_size = [600]\n","latent_dimension = 128\n","dropout_prob = 0.5\n","anneal_cap = 0.2\n","total_anneal_steps = 200000\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.GENERAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.PAIRWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","\n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = False\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","mlp_hidden_size = [600]\n","latent_dimension = 128\n","dropout_prob = 0.5\n","anneal_cap = 0.2\n","total_anneal_steps = 200000\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.GENERAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.PAIRWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","11 Dec 12:21 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","11 Dec 12:21 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","11 Dec 12:21 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","11 Dec 12:21 WARNING Max value of user's history interaction records has reached 20.9471766848816% of the total.\n","Max value of user's history interaction records has reached 20.9471766848816% of the total.\n","11 Dec 12:21 INFO MultiVAE(\n"," (encoder): Sequential(\n"," (0): Linear(in_features=3294, out_features=600, bias=True)\n"," (1): Tanh()\n"," (2): Linear(in_features=600, out_features=128, bias=True)\n"," )\n"," (decoder): Sequential(\n"," (0): Linear(in_features=64, out_features=600, bias=True)\n"," (1): Tanh()\n"," (2): Linear(in_features=600, out_features=3294, bias=True)\n"," )\n",")\n","Trainable parameters: 4072622\n","MultiVAE(\n"," (encoder): Sequential(\n"," (0): Linear(in_features=3294, out_features=600, bias=True)\n"," (1): Tanh()\n"," (2): Linear(in_features=600, out_features=128, bias=True)\n"," )\n"," (decoder): Sequential(\n"," (0): Linear(in_features=64, out_features=600, bias=True)\n"," (1): Tanh()\n"," (2): Linear(in_features=600, out_features=3294, bias=True)\n"," )\n",")\n","Trainable parameters: 4072622\n","11 Dec 12:21 INFO FLOPs: 4068000.0\n","FLOPs: 4068000.0\n","11 Dec 12:21 INFO epoch 0 training [time: 2.16s, train loss: 3249.3142]\n","epoch 0 training [time: 2.16s, train loss: 3249.3142]\n","11 Dec 12:21 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:21 INFO epoch 1 training [time: 1.96s, train loss: 3098.4010]\n","epoch 1 training [time: 1.96s, train loss: 3098.4010]\n","11 Dec 12:21 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:21 INFO epoch 2 training [time: 1.97s, train loss: 3045.1938]\n","epoch 2 training [time: 1.97s, train loss: 3045.1938]\n","11 Dec 12:21 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:21 INFO epoch 3 training [time: 2.02s, train loss: 3008.0520]\n","epoch 3 training [time: 2.02s, train loss: 3008.0520]\n","11 Dec 12:21 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:21 INFO epoch 4 training [time: 2.58s, train loss: 2949.4743]\n","epoch 4 training [time: 2.58s, train loss: 2949.4743]\n","11 Dec 12:21 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:21 INFO epoch 5 training [time: 2.14s, train loss: 2917.6707]\n","epoch 5 training [time: 2.14s, train loss: 2917.6707]\n","11 Dec 12:21 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:22 INFO epoch 6 training [time: 2.28s, train loss: 2897.4954]\n","epoch 6 training [time: 2.28s, train loss: 2897.4954]\n","11 Dec 12:22 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:22 INFO epoch 7 training [time: 2.03s, train loss: 2885.5641]\n","epoch 7 training [time: 2.03s, train loss: 2885.5641]\n","11 Dec 12:22 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:22 INFO epoch 8 training [time: 2.38s, train loss: 2871.9012]\n","epoch 8 training [time: 2.38s, train loss: 2871.9012]\n","11 Dec 12:22 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:22 INFO epoch 9 training [time: 2.46s, train loss: 2851.2055]\n","epoch 9 training [time: 2.46s, train loss: 2851.2055]\n","11 Dec 12:22 INFO Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Saving current: saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:22 INFO Loading model structure and parameters from saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","Loading model structure and parameters from saved/MultiVAE-Dec-11-2023_12-21-45.pth\n","11 Dec 12:22 INFO The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 74.20 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.08 G/8.00 G |\n","+-------------+---------------+\n","The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 74.20 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.08 G/8.00 G |\n","+-------------+---------------+\n","11 Dec 12:22 INFO best valid : None\n","best valid : None\n","11 Dec 12:22 INFO test result: OrderedDict([('recall@10', 0.0839), ('mrr@10', 0.1687), ('ndcg@10', 0.0823), ('hit@10', 0.3494), ('precision@10', 0.0465)])\n","test result: OrderedDict([('recall@10', 0.0839), ('mrr@10', 0.1687), ('ndcg@10', 0.0823), ('hit@10', 0.3494), ('precision@10', 0.0465)])\n"]},{"name":"stdout","output_type":"stream","text":["It took 3.87 mins\n","{'best_valid_score': -inf, 'valid_score_bigger': True, 'best_valid_result': None, 'test_result': OrderedDict([('recall@10', 0.0839), ('mrr@10', 0.1687), ('ndcg@10', 0.0823), ('hit@10', 0.3494), ('precision@10', 0.0465)])}\n","running RecVAE...\n"]},{"name":"stderr","output_type":"stream","text":["11 Dec 12:22 INFO ['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","11 Dec 12:22 INFO \n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = False\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","hidden_dimension = 600\n","latent_dimension = 200\n","dropout_prob = 0.5\n","beta = 0.2\n","gamma = 0.005\n","mixture_weights = [0.15, 0.75, 0.1]\n","n_enc_epochs = 3\n","n_dec_epochs = 1\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.GENERAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.PAIRWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","\n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = False\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","hidden_dimension = 600\n","latent_dimension = 200\n","dropout_prob = 0.5\n","beta = 0.2\n","gamma = 0.005\n","mixture_weights = [0.15, 0.75, 0.1]\n","n_enc_epochs = 3\n","n_dec_epochs = 1\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.GENERAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.PAIRWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","11 Dec 12:25 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","11 Dec 12:25 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'uniform', 'sample_num': 1, 'alpha': 1.0, 'dynamic': False, 'candidate_num': 0}]\n","11 Dec 12:25 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","11 Dec 12:25 WARNING Max value of user's history interaction records has reached 20.9471766848816% of the total.\n","Max value of user's history interaction records has reached 20.9471766848816% of the total.\n","11 Dec 12:25 INFO RecVAE(\n"," (encoder): Encoder(\n"," (fc1): Linear(in_features=3294, out_features=600, bias=True)\n"," (ln1): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc2): Linear(in_features=600, out_features=600, bias=True)\n"," (ln2): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc3): Linear(in_features=600, out_features=600, bias=True)\n"," (ln3): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc4): Linear(in_features=600, out_features=600, bias=True)\n"," (ln4): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc5): Linear(in_features=600, out_features=600, bias=True)\n"," (ln5): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc_mu): Linear(in_features=600, out_features=200, bias=True)\n"," (fc_logvar): Linear(in_features=600, out_features=200, bias=True)\n"," )\n"," (prior): CompositePrior(\n"," (encoder_old): Encoder(\n"," (fc1): Linear(in_features=3294, out_features=600, bias=True)\n"," (ln1): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc2): Linear(in_features=600, out_features=600, bias=True)\n"," (ln2): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc3): Linear(in_features=600, out_features=600, bias=True)\n"," (ln3): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc4): Linear(in_features=600, out_features=600, bias=True)\n"," (ln4): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc5): Linear(in_features=600, out_features=600, bias=True)\n"," (ln5): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc_mu): Linear(in_features=600, out_features=200, bias=True)\n"," (fc_logvar): Linear(in_features=600, out_features=200, bias=True)\n"," )\n"," )\n"," (decoder): Linear(in_features=200, out_features=3294, bias=True)\n",")\n","Trainable parameters: 4327894\n","RecVAE(\n"," (encoder): Encoder(\n"," (fc1): Linear(in_features=3294, out_features=600, bias=True)\n"," (ln1): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc2): Linear(in_features=600, out_features=600, bias=True)\n"," (ln2): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc3): Linear(in_features=600, out_features=600, bias=True)\n"," (ln3): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc4): Linear(in_features=600, out_features=600, bias=True)\n"," (ln4): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc5): Linear(in_features=600, out_features=600, bias=True)\n"," (ln5): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc_mu): Linear(in_features=600, out_features=200, bias=True)\n"," (fc_logvar): Linear(in_features=600, out_features=200, bias=True)\n"," )\n"," (prior): CompositePrior(\n"," (encoder_old): Encoder(\n"," (fc1): Linear(in_features=3294, out_features=600, bias=True)\n"," (ln1): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc2): Linear(in_features=600, out_features=600, bias=True)\n"," (ln2): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc3): Linear(in_features=600, out_features=600, bias=True)\n"," (ln3): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc4): Linear(in_features=600, out_features=600, bias=True)\n"," (ln4): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc5): Linear(in_features=600, out_features=600, bias=True)\n"," (ln5): LayerNorm((600,), eps=0.1, elementwise_affine=True)\n"," (fc_mu): Linear(in_features=600, out_features=200, bias=True)\n"," (fc_logvar): Linear(in_features=600, out_features=200, bias=True)\n"," )\n"," )\n"," (decoder): Linear(in_features=200, out_features=3294, bias=True)\n",")\n","Trainable parameters: 4327894\n","11 Dec 12:25 INFO FLOPs: 4321200.0\n","FLOPs: 4321200.0\n","11 Dec 12:25 INFO epoch 0 training [time: 23.87s, train loss: 2354.4009]\n","epoch 0 training [time: 23.87s, train loss: 2354.4009]\n","11 Dec 12:25 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:26 INFO epoch 1 training [time: 26.41s, train loss: 2247.2854]\n","epoch 1 training [time: 26.41s, train loss: 2247.2854]\n","11 Dec 12:26 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:26 INFO epoch 2 training [time: 26.19s, train loss: 2184.4206]\n","epoch 2 training [time: 26.19s, train loss: 2184.4206]\n","11 Dec 12:26 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:27 INFO epoch 3 training [time: 28.26s, train loss: 2147.9836]\n","epoch 3 training [time: 28.26s, train loss: 2147.9836]\n","11 Dec 12:27 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:27 INFO epoch 4 training [time: 25.59s, train loss: 2108.6837]\n","epoch 4 training [time: 25.59s, train loss: 2108.6837]\n","11 Dec 12:27 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:27 INFO epoch 5 training [time: 19.99s, train loss: 2073.2995]\n","epoch 5 training [time: 19.99s, train loss: 2073.2995]\n","11 Dec 12:27 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:28 INFO epoch 6 training [time: 23.58s, train loss: 2043.1616]\n","epoch 6 training [time: 23.58s, train loss: 2043.1616]\n","11 Dec 12:28 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:28 INFO epoch 7 training [time: 24.14s, train loss: 2013.9314]\n","epoch 7 training [time: 24.14s, train loss: 2013.9314]\n","11 Dec 12:28 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:28 INFO epoch 8 training [time: 12.42s, train loss: 1998.7426]\n","epoch 8 training [time: 12.42s, train loss: 1998.7426]\n","11 Dec 12:28 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:28 INFO epoch 9 training [time: 10.69s, train loss: 1973.2974]\n","epoch 9 training [time: 10.69s, train loss: 1973.2974]\n","11 Dec 12:28 INFO Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Saving current: saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:28 INFO Loading model structure and parameters from saved/RecVAE-Dec-11-2023_12-25-16.pth\n","Loading model structure and parameters from saved/RecVAE-Dec-11-2023_12-25-16.pth\n","11 Dec 12:29 INFO The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 50.10 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.09 G/8.00 G |\n","+-------------+---------------+\n","The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 50.10 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.09 G/8.00 G |\n","+-------------+---------------+\n","11 Dec 12:29 INFO best valid : None\n","best valid : None\n","11 Dec 12:29 INFO test result: OrderedDict([('recall@10', 0.0844), ('mrr@10', 0.1662), ('ndcg@10', 0.0816), ('hit@10', 0.3519), ('precision@10', 0.0468)])\n","test result: OrderedDict([('recall@10', 0.0844), ('mrr@10', 0.1662), ('ndcg@10', 0.0816), ('hit@10', 0.3519), ('precision@10', 0.0468)])\n"]},{"name":"stdout","output_type":"stream","text":["It took 6.70 mins\n","{'best_valid_score': -inf, 'valid_score_bigger': True, 'best_valid_result': None, 'test_result': OrderedDict([('recall@10', 0.0844), ('mrr@10', 0.1662), ('ndcg@10', 0.0816), ('hit@10', 0.3519), ('precision@10', 0.0468)])}\n","CPU times: user 25min 39s, sys: 9min 10s, total: 34min 49s\n","Wall time: 32min 31s\n"]}],"source":["%%time\n","model_list = [ \"LightGCN\", \"MultiVAE\", \"RecVAE\"] \n","\n","for model_name in model_list:\n"," print(f\"running {model_name}...\")\n"," start = time.time()\n"," result = run_recbole(model=model_name, dataset = 'recbox_data',config_dict = parameter_dict)\n"," t = time.time() - start\n"," print(f\"It took {t/60:.2f} mins\")\n"," print(result)"]},{"cell_type":"code","execution_count":35,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Collecting kmeans-pytorch\n"," Downloading kmeans_pytorch-0.3-py3-none-any.whl (4.4 kB)\n","Installing collected packages: kmeans-pytorch\n","Successfully installed kmeans-pytorch-0.3\n","Note: you may need to restart the kernel to use updated packages.\n"]}],"source":["%pip install kmeans-pytorch"]},{"cell_type":"code","execution_count":36,"metadata":{},"outputs":[],"source":["from kmeans_pytorch import kmeans"]},{"cell_type":"code","execution_count":37,"metadata":{"execution":{"iopub.execute_input":"2023-01-22T18:14:48.482175Z","iopub.status.busy":"2023-01-22T18:14:48.481796Z","iopub.status.idle":"2023-01-22T19:32:27.636297Z","shell.execute_reply":"2023-01-22T19:32:27.635371Z","shell.execute_reply.started":"2023-01-22T18:14:48.482143Z"},"trusted":true},"outputs":[{"name":"stdout","output_type":"stream","text":["running CORE...\n"]},{"name":"stderr","output_type":"stream","text":["11 Dec 13:40 INFO ['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","11 Dec 13:40 INFO \n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = True\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","embedding_size = 64\n","inner_size = 256\n","n_layers = 2\n","n_heads = 2\n","hidden_dropout_prob = 0.5\n","attn_dropout_prob = 0.5\n","hidden_act = gelu\n","layer_norm_eps = 1e-12\n","initializer_range = 0.02\n","loss_type = CE\n","dnn_type = trm\n","sess_dropout = 0.2\n","item_dropout = 0.2\n","temperature = 0.07\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.SEQUENTIAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.POINTWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","\n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = True\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","embedding_size = 64\n","inner_size = 256\n","n_layers = 2\n","n_heads = 2\n","hidden_dropout_prob = 0.5\n","attn_dropout_prob = 0.5\n","hidden_act = gelu\n","layer_norm_eps = 1e-12\n","initializer_range = 0.02\n","loss_type = CE\n","dnn_type = trm\n","sess_dropout = 0.2\n","item_dropout = 0.2\n","temperature = 0.07\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.SEQUENTIAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.POINTWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","11 Dec 13:41 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","11 Dec 13:42 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}]\n","11 Dec 13:42 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","11 Dec 13:42 INFO CORE(\n"," (sess_dropout): Dropout(p=0.2, inplace=False)\n"," (item_dropout): Dropout(p=0.2, inplace=False)\n"," (item_embedding): Embedding(3294, 64, padding_idx=0)\n"," (net): TransNet(\n"," (position_embedding): Embedding(50, 64)\n"," (trm_encoder): TransformerEncoder(\n"," (layer): ModuleList(\n"," (0-1): 2 x TransformerLayer(\n"," (multi_head_attention): MultiHeadAttention(\n"," (query): Linear(in_features=64, out_features=64, bias=True)\n"," (key): Linear(in_features=64, out_features=64, bias=True)\n"," (value): Linear(in_features=64, out_features=64, bias=True)\n"," (softmax): Softmax(dim=-1)\n"," (attn_dropout): Dropout(p=0.5, inplace=False)\n"," (dense): Linear(in_features=64, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (out_dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," (feed_forward): FeedForward(\n"," (dense_1): Linear(in_features=64, out_features=256, bias=True)\n"," (dense_2): Linear(in_features=256, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," )\n"," )\n"," )\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," (fn): Linear(in_features=64, out_features=1, bias=True)\n"," )\n"," (loss_fct): CrossEntropyLoss()\n",")\n","Trainable parameters: 314177\n","CORE(\n"," (sess_dropout): Dropout(p=0.2, inplace=False)\n"," (item_dropout): Dropout(p=0.2, inplace=False)\n"," (item_embedding): Embedding(3294, 64, padding_idx=0)\n"," (net): TransNet(\n"," (position_embedding): Embedding(50, 64)\n"," (trm_encoder): TransformerEncoder(\n"," (layer): ModuleList(\n"," (0-1): 2 x TransformerLayer(\n"," (multi_head_attention): MultiHeadAttention(\n"," (query): Linear(in_features=64, out_features=64, bias=True)\n"," (key): Linear(in_features=64, out_features=64, bias=True)\n"," (value): Linear(in_features=64, out_features=64, bias=True)\n"," (softmax): Softmax(dim=-1)\n"," (attn_dropout): Dropout(p=0.5, inplace=False)\n"," (dense): Linear(in_features=64, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (out_dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," (feed_forward): FeedForward(\n"," (dense_1): Linear(in_features=64, out_features=256, bias=True)\n"," (dense_2): Linear(in_features=256, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," )\n"," )\n"," )\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," (fn): Linear(in_features=64, out_features=1, bias=True)\n"," )\n"," (loss_fct): CrossEntropyLoss()\n",")\n","Trainable parameters: 314177\n","11 Dec 13:42 INFO FLOPs: 4986664.0\n","FLOPs: 4986664.0\n","11 Dec 14:56 INFO epoch 0 training [time: 4465.02s, train loss: 3014.5362]\n","epoch 0 training [time: 4465.02s, train loss: 3014.5362]\n","11 Dec 14:56 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 15:22 INFO epoch 1 training [time: 1551.87s, train loss: 2695.9846]\n","epoch 1 training [time: 1551.87s, train loss: 2695.9846]\n","11 Dec 15:22 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 15:51 INFO epoch 2 training [time: 1725.13s, train loss: 2613.6121]\n","epoch 2 training [time: 1725.13s, train loss: 2613.6121]\n","11 Dec 15:51 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 16:20 INFO epoch 3 training [time: 1788.98s, train loss: 2579.6137]\n","epoch 3 training [time: 1788.98s, train loss: 2579.6137]\n","11 Dec 16:20 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 17:53 INFO epoch 4 training [time: 5548.65s, train loss: 2562.4357]\n","epoch 4 training [time: 5548.65s, train loss: 2562.4357]\n","11 Dec 17:53 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 18:23 INFO epoch 5 training [time: 1835.07s, train loss: 2551.7563]\n","epoch 5 training [time: 1835.07s, train loss: 2551.7563]\n","11 Dec 18:23 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 18:52 INFO epoch 6 training [time: 1691.97s, train loss: 2545.4003]\n","epoch 6 training [time: 1691.97s, train loss: 2545.4003]\n","11 Dec 18:52 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 19:23 INFO epoch 7 training [time: 1858.18s, train loss: 2540.3678]\n","epoch 7 training [time: 1858.18s, train loss: 2540.3678]\n","11 Dec 19:23 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 20:06 INFO epoch 8 training [time: 2614.77s, train loss: 2537.1684]\n","epoch 8 training [time: 2614.77s, train loss: 2537.1684]\n","11 Dec 20:06 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 21:22 INFO epoch 9 training [time: 4561.69s, train loss: 2534.2584]\n","epoch 9 training [time: 4561.69s, train loss: 2534.2584]\n","11 Dec 21:22 INFO Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","Saving current: saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 21:22 INFO Loading model structure and parameters from saved/CORE-Dec-11-2023_13-42-03.pth\n","Loading model structure and parameters from saved/CORE-Dec-11-2023_13-42-03.pth\n","11 Dec 21:23 INFO The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 46.10 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.77 G/8.00 G |\n","+-------------+---------------+\n","The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 46.10 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.77 G/8.00 G |\n","+-------------+---------------+\n","11 Dec 21:23 INFO best valid : None\n","best valid : None\n","11 Dec 21:23 INFO test result: OrderedDict([('recall@10', 0.0921), ('mrr@10', 0.0297), ('ndcg@10', 0.044), ('hit@10', 0.0921), ('precision@10', 0.0092)])\n","test result: OrderedDict([('recall@10', 0.0921), ('mrr@10', 0.0297), ('ndcg@10', 0.044), ('hit@10', 0.0921), ('precision@10', 0.0092)])\n"]},{"name":"stdout","output_type":"stream","text":["It took 463.62 mins\n","{'best_valid_score': -inf, 'valid_score_bigger': True, 'best_valid_result': None, 'test_result': OrderedDict([('recall@10', 0.0921), ('mrr@10', 0.0297), ('ndcg@10', 0.044), ('hit@10', 0.0921), ('precision@10', 0.0092)])}\n","running LightSANs...\n"]},{"name":"stderr","output_type":"stream","text":["11 Dec 21:23 INFO ['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","11 Dec 21:23 INFO \n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = True\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","k_interests = 5\n","n_layers = 2\n","n_heads = 2\n","hidden_size = 64\n","inner_size = 256\n","hidden_dropout_prob = 0.5\n","attn_dropout_prob = 0.5\n","hidden_act = gelu\n","layer_norm_eps = 1e-12\n","initializer_range = 0.02\n","loss_type = CE\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.SEQUENTIAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.POINTWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","\n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = True\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","k_interests = 5\n","n_layers = 2\n","n_heads = 2\n","hidden_size = 64\n","inner_size = 256\n","hidden_dropout_prob = 0.5\n","attn_dropout_prob = 0.5\n","hidden_act = gelu\n","layer_norm_eps = 1e-12\n","initializer_range = 0.02\n","loss_type = CE\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.SEQUENTIAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.POINTWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","11 Dec 21:31 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","11 Dec 21:31 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}]\n","11 Dec 21:31 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","11 Dec 21:31 INFO LightSANs(\n"," (item_embedding): Embedding(3294, 64, padding_idx=0)\n"," (position_embedding): Embedding(50, 64)\n"," (trm_encoder): LightTransformerEncoder(\n"," (layer): ModuleList(\n"," (0-1): 2 x LightTransformerLayer(\n"," (multi_head_attention): LightMultiHeadAttention(\n"," (query): Linear(in_features=64, out_features=64, bias=True)\n"," (key): Linear(in_features=64, out_features=64, bias=True)\n"," (value): Linear(in_features=64, out_features=64, bias=True)\n"," (attpooling_key): ItemToInterestAggregation()\n"," (attpooling_value): ItemToInterestAggregation()\n"," (pos_q_linear): Linear(in_features=64, out_features=64, bias=True)\n"," (pos_k_linear): Linear(in_features=64, out_features=64, bias=True)\n"," (pos_ln): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (attn_dropout): Dropout(p=0.5, inplace=False)\n"," (dense): Linear(in_features=64, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (out_dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," (feed_forward): FeedForward(\n"," (dense_1): Linear(in_features=64, out_features=256, bias=True)\n"," (dense_2): Linear(in_features=256, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," )\n"," )\n"," )\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," (loss_fct): CrossEntropyLoss()\n",")\n","Trainable parameters: 332288\n","LightSANs(\n"," (item_embedding): Embedding(3294, 64, padding_idx=0)\n"," (position_embedding): Embedding(50, 64)\n"," (trm_encoder): LightTransformerEncoder(\n"," (layer): ModuleList(\n"," (0-1): 2 x LightTransformerLayer(\n"," (multi_head_attention): LightMultiHeadAttention(\n"," (query): Linear(in_features=64, out_features=64, bias=True)\n"," (key): Linear(in_features=64, out_features=64, bias=True)\n"," (value): Linear(in_features=64, out_features=64, bias=True)\n"," (attpooling_key): ItemToInterestAggregation()\n"," (attpooling_value): ItemToInterestAggregation()\n"," (pos_q_linear): Linear(in_features=64, out_features=64, bias=True)\n"," (pos_k_linear): Linear(in_features=64, out_features=64, bias=True)\n"," (pos_ln): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (attn_dropout): Dropout(p=0.5, inplace=False)\n"," (dense): Linear(in_features=64, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (out_dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," (feed_forward): FeedForward(\n"," (dense_1): Linear(in_features=64, out_features=256, bias=True)\n"," (dense_2): Linear(in_features=256, out_features=64, bias=True)\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," )\n"," )\n"," )\n"," )\n"," (LayerNorm): LayerNorm((64,), eps=1e-12, elementwise_affine=True)\n"," (dropout): Dropout(p=0.5, inplace=False)\n"," (loss_fct): CrossEntropyLoss()\n",")\n","Trainable parameters: 332288\n","11 Dec 21:31 INFO FLOPs: 5785664.0\n","FLOPs: 5785664.0\n","11 Dec 22:10 INFO epoch 0 training [time: 2297.51s, train loss: 2745.5162]\n","epoch 0 training [time: 2297.51s, train loss: 2745.5162]\n","11 Dec 22:10 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","11 Dec 22:35 INFO epoch 1 training [time: 1523.42s, train loss: 2594.7601]\n","epoch 1 training [time: 1523.42s, train loss: 2594.7601]\n","11 Dec 22:35 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","11 Dec 23:00 INFO epoch 2 training [time: 1474.23s, train loss: 2552.7842]\n","epoch 2 training [time: 1474.23s, train loss: 2552.7842]\n","11 Dec 23:00 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","11 Dec 23:24 INFO epoch 3 training [time: 1459.72s, train loss: 2529.9439]\n","epoch 3 training [time: 1459.72s, train loss: 2529.9439]\n","11 Dec 23:24 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","11 Dec 23:47 INFO epoch 4 training [time: 1402.02s, train loss: 2516.8341]\n","epoch 4 training [time: 1402.02s, train loss: 2516.8341]\n","11 Dec 23:47 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 00:44 INFO epoch 5 training [time: 3408.17s, train loss: 2508.0956]\n","epoch 5 training [time: 3408.17s, train loss: 2508.0956]\n","12 Dec 00:44 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 01:06 INFO epoch 6 training [time: 1326.43s, train loss: 2501.2301]\n","epoch 6 training [time: 1326.43s, train loss: 2501.2301]\n","12 Dec 01:06 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 02:02 INFO epoch 7 training [time: 3351.58s, train loss: 2496.2055]\n","epoch 7 training [time: 3351.58s, train loss: 2496.2055]\n","12 Dec 02:02 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 06:08 INFO epoch 8 training [time: 14777.87s, train loss: 2491.3191]\n","epoch 8 training [time: 14777.87s, train loss: 2491.3191]\n","12 Dec 06:08 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 08:40 INFO epoch 9 training [time: 9116.53s, train loss: 2487.0272]\n","epoch 9 training [time: 9116.53s, train loss: 2487.0272]\n","12 Dec 08:40 INFO Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Saving current: saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 08:40 INFO Loading model structure and parameters from saved/LightSANs-Dec-11-2023_21-31-57.pth\n","Loading model structure and parameters from saved/LightSANs-Dec-11-2023_21-31-57.pth\n","12 Dec 08:41 INFO The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 30.70 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.82 G/8.00 G |\n","+-------------+---------------+\n","The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 30.70 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.82 G/8.00 G |\n","+-------------+---------------+\n","12 Dec 08:41 INFO best valid : None\n","best valid : None\n","12 Dec 08:41 INFO test result: OrderedDict([('recall@10', 0.1029), ('mrr@10', 0.0358), ('ndcg@10', 0.0513), ('hit@10', 0.1029), ('precision@10', 0.0103)])\n","test result: OrderedDict([('recall@10', 0.1029), ('mrr@10', 0.0358), ('ndcg@10', 0.0513), ('hit@10', 0.1029), ('precision@10', 0.0103)])\n"]},{"name":"stdout","output_type":"stream","text":["It took 677.87 mins\n","{'best_valid_score': -inf, 'valid_score_bigger': True, 'best_valid_result': None, 'test_result': OrderedDict([('recall@10', 0.1029), ('mrr@10', 0.0358), ('ndcg@10', 0.0513), ('hit@10', 0.1029), ('precision@10', 0.0103)])}\n","running NextItNet...\n"]},{"name":"stderr","output_type":"stream","text":["12 Dec 08:41 INFO ['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","['/Users/annapikuleva/Library/Python/3.9/lib/python/site-packages/ipykernel_launcher.py', '--f=/Users/annapikuleva/Library/Jupyter/runtime/kernel-v2-3832937JAU6uqtVOE.json']\n","12 Dec 08:41 INFO \n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = True\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","embedding_size = 64\n","kernel_size = 3\n","block_num = 5\n","dilations = [1, 4]\n","reg_weight = 1e-05\n","loss_type = CE\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.SEQUENTIAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.POINTWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","\n","General Hyper Parameters:\n","gpu_id = 0\n","use_gpu = True\n","seed = 2020\n","state = INFO\n","reproducibility = True\n","data_path = recbox_data\n","checkpoint_dir = saved\n","show_progress = False\n","save_dataset = False\n","dataset_save_path = None\n","save_dataloaders = False\n","dataloaders_save_path = None\n","log_wandb = False\n","\n","Training Hyper Parameters:\n","epochs = 10\n","train_batch_size = 2048\n","learner = adam\n","learning_rate = 0.001\n","train_neg_sample_args = {'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}\n","eval_step = 1\n","stopping_step = 10\n","clip_grad_norm = None\n","weight_decay = 0.0\n","loss_decimal_place = 4\n","\n","Evaluation Hyper Parameters:\n","eval_args = {'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}\n","repeatable = True\n","metrics = ['Recall', 'MRR', 'NDCG', 'Hit', 'Precision']\n","topk = [10]\n","valid_metric = MRR@10\n","valid_metric_bigger = True\n","eval_batch_size = 4096\n","metric_decimal_place = 4\n","\n","Dataset Hyper Parameters:\n","field_separator = \t\n","seq_separator = \n","USER_ID_FIELD = user_id\n","ITEM_ID_FIELD = item_id\n","RATING_FIELD = rating\n","TIME_FIELD = timestamp\n","seq_len = None\n","LABEL_FIELD = label\n","threshold = None\n","NEG_PREFIX = neg_\n","load_col = {'inter': ['user_id', 'item_id', 'timestamp']}\n","unload_col = None\n","unused_col = None\n","additional_feat_suffix = None\n","rm_dup_inter = None\n","val_interval = None\n","filter_inter_by_user_or_item = True\n","user_inter_num_interval = [40,inf)\n","item_inter_num_interval = [40,inf)\n","alias_of_user_id = None\n","alias_of_item_id = None\n","alias_of_entity_id = None\n","alias_of_relation_id = None\n","preload_weight = None\n","normalize_field = None\n","normalize_all = None\n","ITEM_LIST_LENGTH_FIELD = item_length\n","LIST_SUFFIX = _list\n","MAX_ITEM_LIST_LENGTH = 50\n","POSITION_FIELD = position_id\n","HEAD_ENTITY_ID_FIELD = head_id\n","TAIL_ENTITY_ID_FIELD = tail_id\n","RELATION_ID_FIELD = relation_id\n","ENTITY_ID_FIELD = entity_id\n","benchmark_filename = None\n","\n","Other Hyper Parameters: \n","worker = 0\n","wandb_project = recbole\n","shuffle = True\n","require_pow = False\n","enable_amp = False\n","enable_scaler = False\n","transform = None\n","embedding_size = 64\n","kernel_size = 3\n","block_num = 5\n","dilations = [1, 4]\n","reg_weight = 1e-05\n","loss_type = CE\n","numerical_features = []\n","discretization = None\n","kg_reverse_r = False\n","entity_kg_num_interval = [0,inf)\n","relation_kg_num_interval = [0,inf)\n","MODEL_TYPE = ModelType.SEQUENTIAL\n","device = cpu\n","neg_sampling = None\n","verbose = -1\n","MODEL_INPUT_TYPE = InputType.POINTWISE\n","eval_type = EvaluatorType.RANKING\n","single_spec = True\n","local_rank = 0\n","valid_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","test_neg_sample_args = {'distribution': 'uniform', 'sample_num': 'none'}\n","\n","\n","12 Dec 08:43 INFO recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","recbox_data\n","The number of users: 13355\n","Average actions of users: 63.815710648494836\n","The number of items: 3294\n","Average actions of items: 258.78985727300335\n","The number of inters: 852195\n","The sparsity of the dataset: 98.06281322904924%\n","Remain Fields: ['user_id', 'item_id', 'timestamp']\n","12 Dec 08:43 INFO [Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}]\n","[Training]: train_batch_size = [2048] train_neg_sample_args: [{'distribution': 'none', 'sample_num': 'none', 'alpha': 'none', 'dynamic': False, 'candidate_num': 0}]\n","12 Dec 08:43 INFO [Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","[Evaluation]: eval_batch_size = [4096] eval_args: [{'split': {'RS': [9, 0, 1]}, 'order': 'TO', 'group_by': 'user', 'mode': {'valid': 'full', 'test': 'full'}}]\n","12 Dec 08:43 INFO NextItNet(\n"," (item_embedding): Embedding(3294, 64, padding_idx=0)\n"," (residual_blocks): Sequential(\n"," (0): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (1): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (2): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (3): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (4): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (5): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (6): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (7): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (8): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (9): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," )\n"," (final_layer): Linear(in_features=64, out_features=64, bias=True)\n"," (loss_fct): CrossEntropyLoss()\n"," (reg_loss): RegLoss()\n",")\n","Trainable parameters: 464576\n","NextItNet(\n"," (item_embedding): Embedding(3294, 64, padding_idx=0)\n"," (residual_blocks): Sequential(\n"," (0): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (1): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (2): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (3): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (4): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (5): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (6): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (7): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (8): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(2, 2))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," (9): ResidualBlock_b(\n"," (conv1): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(4, 4))\n"," (ln1): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," (conv2): Conv2d(64, 64, kernel_size=(1, 3), stride=(1, 1), dilation=(8, 8))\n"," (ln2): LayerNorm((64,), eps=1e-08, elementwise_affine=True)\n"," )\n"," )\n"," (final_layer): Linear(in_features=64, out_features=64, bias=True)\n"," (loss_fct): CrossEntropyLoss()\n"," (reg_loss): RegLoss()\n",")\n","Trainable parameters: 464576\n","12 Dec 08:43 INFO FLOPs: 12423360.0\n","FLOPs: 12423360.0\n","12 Dec 10:08 INFO epoch 0 training [time: 5095.82s, train loss: 2732.6105]\n","epoch 0 training [time: 5095.82s, train loss: 2732.6105]\n","12 Dec 10:08 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 11:33 INFO epoch 1 training [time: 5097.32s, train loss: 2601.4325]\n","epoch 1 training [time: 5097.32s, train loss: 2601.4325]\n","12 Dec 11:33 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 13:31 INFO epoch 2 training [time: 7077.48s, train loss: 2554.3704]\n","epoch 2 training [time: 7077.48s, train loss: 2554.3704]\n","12 Dec 13:31 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 14:39 INFO epoch 3 training [time: 4117.24s, train loss: 2529.2335]\n","epoch 3 training [time: 4117.24s, train loss: 2529.2335]\n","12 Dec 14:39 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 17:57 INFO epoch 4 training [time: 11838.39s, train loss: 2512.5584]\n","epoch 4 training [time: 11838.39s, train loss: 2512.5584]\n","12 Dec 17:57 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 18:42 INFO epoch 5 training [time: 2724.19s, train loss: 2497.9890]\n","epoch 5 training [time: 2724.19s, train loss: 2497.9890]\n","12 Dec 18:42 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 19:30 INFO epoch 6 training [time: 2856.86s, train loss: 2485.4469]\n","epoch 6 training [time: 2856.86s, train loss: 2485.4469]\n","12 Dec 19:30 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 20:13 INFO epoch 7 training [time: 2609.22s, train loss: 2474.9533]\n","epoch 7 training [time: 2609.22s, train loss: 2474.9533]\n","12 Dec 20:13 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 21:18 INFO epoch 8 training [time: 3904.76s, train loss: 2465.3467]\n","epoch 8 training [time: 3904.76s, train loss: 2465.3467]\n","12 Dec 21:18 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 22:13 INFO epoch 9 training [time: 3272.21s, train loss: 2456.8602]\n","epoch 9 training [time: 3272.21s, train loss: 2456.8602]\n","12 Dec 22:13 INFO Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Saving current: saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 22:13 INFO Loading model structure and parameters from saved/NextItNet-Dec-12-2023_08-43-30.pth\n","Loading model structure and parameters from saved/NextItNet-Dec-12-2023_08-43-30.pth\n","12 Dec 22:16 INFO The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 11.80 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.74 G/8.00 G |\n","+-------------+---------------+\n","The running environment of this training is as follows:\n","+-------------+---------------+\n","| Environment | Usage |\n","+=============+===============+\n","| CPU | 11.80 % |\n","+-------------+---------------+\n","| GPU | 0.0 / 0.0 |\n","+-------------+---------------+\n","| Memory | 0.74 G/8.00 G |\n","+-------------+---------------+\n","12 Dec 22:16 INFO best valid : None\n","best valid : None\n","12 Dec 22:16 INFO test result: OrderedDict([('recall@10', 0.0922), ('mrr@10', 0.0329), ('ndcg@10', 0.0466), ('hit@10', 0.0922), ('precision@10', 0.0092)])\n","test result: OrderedDict([('recall@10', 0.0922), ('mrr@10', 0.0329), ('ndcg@10', 0.0466), ('hit@10', 0.0922), ('precision@10', 0.0092)])\n"]},{"name":"stdout","output_type":"stream","text":["It took 814.55 mins\n","{'best_valid_score': -inf, 'valid_score_bigger': True, 'best_valid_result': None, 'test_result': OrderedDict([('recall@10', 0.0922), ('mrr@10', 0.0329), ('ndcg@10', 0.0466), ('hit@10', 0.0922), ('precision@10', 0.0092)])}\n","CPU times: user 1d 24min 58s, sys: 13h 28min 5s, total: 1d 13h 53min 4s\n","Wall time: 1d 8h 36min 2s\n"]}],"source":["%%time\n","model_list = [\"CORE\", \"LightSANs\", \"NextItNet\",] \n","\n","parameter_dict[\"train_neg_sample_args\"] = None\n","\n","for model_name in model_list:\n"," print(f\"running {model_name}...\")\n"," start = time.time()\n"," result = run_recbole(model=model_name, dataset = 'recbox_data', config_dict = parameter_dict)\n"," t = time.time() - start\n"," print(f\"It took {t/60:.2f} mins\")\n"," print(result)"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.9.13"},"papermill":{"default_parameters":{},"duration":27491.154881,"end_time":"2022-11-28T00:11:27.624787","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2022-11-27T16:33:16.469906","version":"2.3.4"}},"nbformat":4,"nbformat_minor":5} diff --git a/service/api/views.py b/service/api/views.py index 24cf4a7f..95178c1e 100644 --- a/service/api/views.py +++ b/service/api/views.py @@ -1,20 +1,38 @@ from typing import List - -from fastapi import APIRouter, FastAPI, Request +from fastapi import APIRouter, Depends, FastAPI, Request from pydantic import BaseModel - -from service.api.exceptions import UserNotFoundError +import dill +from service.api.exceptions import ModelNotFoundError, UnauthorizedUserError, UserNotFoundError from service.log import app_logger +import pandas as pd +from service.models import recommend_popular + + +# load predictions of dssm model +dssm_preds = pd.read_csv("dssm_predictions.csv") +dssm_preds.item_id = dssm_preds.item_id.apply(lambda x: [int(i) for i in x[1:-1].split(", ")]) + + +# get popular recommendations +interactions = pd.read_csv('data/interactions.csv') +interactions['last_watch_dt'] = pd.to_datetime(interactions['last_watch_dt']) +interactions.rename( + columns={ + 'last_watch_dt': 'datetime', + 'total_dur': 'weight', + }, + inplace=True, + ) +popular_recs = recommend_popular(interactions) +popular_recs_30 = recommend_popular(interactions, days = 30) class RecoResponse(BaseModel): user_id: int items: List[int] - router = APIRouter() - @router.get( path="/health", tags=["Health"], @@ -23,26 +41,32 @@ async def health() -> str: return "I am alive" + @router.get( path="/reco/{model_name}/{user_id}", tags=["Recommendations"], - response_model=RecoResponse, + response_model=RecoResponse ) async def get_reco( - request: Request, - model_name: str, - user_id: int, -) -> RecoResponse: - app_logger.info(f"Request for model: {model_name}, user_id: {user_id}") - - # Write your code here + request: Request, + model_name: str, + user_id: int, + # token=Depends(bearer) + ) -> RecoResponse: + # app_logger.info(f"Request for model: {model_name}, user_id: {user_id}") + app_logger.info(f"Request for model: {model_name}") + app_logger.info(f"Request for user: {user_id}") if user_id > 10**9: raise UserNotFoundError(error_message=f"User {user_id} not found") + + if model_name == "DSSM": + try: + recs_list = dssm_preds[dssm_preds.user_id == user_id].item_id.values[0] + except: + recs_list = popular_recs_30 - k_recs = request.app.state.k_recs - reco = list(range(k_recs)) - return RecoResponse(user_id=user_id, items=reco) + return RecoResponse(user_id=user_id, items=recs_list) def add_views(app: FastAPI) -> None: diff --git a/userknn.py b/userknn.py new file mode 100644 index 00000000..e7cf55ab --- /dev/null +++ b/userknn.py @@ -0,0 +1,112 @@ +from typing import Dict +from collections import Counter + +import pandas as pd +import numpy as np +import scipy as sp +from implicit.nearest_neighbours import ItemItemRecommender + + +class UserKnn(): + """Class for fit-perdict UserKNN model + based on ItemKNN model from implicit.nearest_neighbours + """ + + def __init__(self, model: ItemItemRecommender, N_users: int = 50): + self.N_users = N_users + self.model = model + self.is_fitted = False + + def get_mappings(self, train): + self.users_inv_mapping = dict(enumerate(train['user_id'].unique())) + self.users_mapping = {v: k for k, v in self.users_inv_mapping.items()} + + self.items_inv_mapping = dict(enumerate(train['item_id'].unique())) + self.items_mapping = {v: k for k, v in self.items_inv_mapping.items()} + + def get_matrix(self, df: pd.DataFrame, + user_col: str = 'user_id', + item_col: str = 'item_id', + weight_col: str = None, + users_mapping: Dict[int, int] = None, + items_mapping: Dict[int, int] = None): + + if weight_col: + weights = df[weight_col].astype(np.float32) + else: + weights = np.ones(len(df), dtype=np.float32) + + self.interaction_matrix = sp.sparse.coo_matrix(( + weights, + ( + df[item_col].map(self.items_mapping.get), + df[user_col].map(self.users_mapping.get) + ) + )) + + self.watched = df\ + .groupby(user_col, as_index=False)\ + .agg({item_col: list})\ + .rename(columns={user_col: 'sim_user_id'}) + + return self.interaction_matrix + + def idf(self, n: int, x: float): + return np.log((1 + n) / (1 + x) + 1) + + def _count_item_idf(self, df: pd.DataFrame): + item_cnt = Counter(df['item_id'].values) + item_idf = pd.DataFrame.from_dict(item_cnt, orient='index', + columns=['doc_freq']).reset_index() + item_idf['idf'] = item_idf['doc_freq'].apply(lambda x: self.idf(self.n, x)) + self.item_idf = item_idf + + def fit(self, train: pd.DataFrame): + self.user_knn = self.model + self.get_mappings(train) + self.weights_matrix = self.get_matrix(train, + users_mapping=self.users_mapping, + items_mapping=self.items_mapping) + + self.n = train.shape[0] + self._count_item_idf(train) + + self.user_knn.fit(self.weights_matrix) + self.is_fitted = True + + def _generate_recs_mapper(self, model: ItemItemRecommender, user_mapping: Dict[int, int], + user_inv_mapping: Dict[int, int], N: int): + def _recs_mapper(user): + user_id = self.users_mapping[user] + users, sim = model.similar_items(user_id, N=N) + return [self.users_inv_mapping[user] for user in users], sim + return _recs_mapper + + def predict(self, test: pd.DataFrame, N_recs: int = 10): + + if not self.is_fitted: + raise ValueError("Please call fit before predict") + + mapper = self._generate_recs_mapper( + model=self.user_knn, + user_mapping=self.users_mapping, + user_inv_mapping=self.users_inv_mapping, + N=self.N_users + ) + + recs = pd.DataFrame({'user_id': test['user_id'].unique()}) + recs['sim_user_id'], recs['sim'] = zip(*recs['user_id'].map(mapper)) + recs = recs.set_index('user_id').apply(pd.Series.explode).reset_index() + + recs = recs[~(recs['user_id'] == recs['sim_user_id'])]\ + .merge(self.watched, on=['sim_user_id'], how='left')\ + .explode('item_id')\ + .sort_values(['user_id', 'sim'], ascending=False)\ + .drop_duplicates(['user_id', 'item_id'], keep='first')\ + .merge(self.item_idf, left_on='item_id', right_on='index', how='left') + + recs['score'] = recs['sim'] * recs['idf'] + recs = recs.sort_values(['user_id', 'score'], ascending=False) + recs['rank'] = recs.groupby('user_id').cumcount() + 1 + return recs[recs['rank'] <= N_recs][['user_id', 'item_id', 'score', 'rank']] + \ No newline at end of file