{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":36171,"databundleVersionId":3543906,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd\n\ndf = pd.read_csv('/kaggle/input/anime-recommendation-system-by-tah/train.csv')\ndf.head()\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:03.919516Z","iopub.execute_input":"2025-07-31T02:12:03.919908Z","iopub.status.idle":"2025-07-31T02:12:06.775257Z","shell.execute_reply.started":"2025-07-31T02:12:03.919881Z","shell.execute_reply":"2025-07-31T02:12:06.774374Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from fastai.collab import *\nfrom fastai.tabular.all import *\nset_seed(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:06.776309Z","iopub.execute_input":"2025-07-31T02:12:06.776600Z","iopub.status.idle":"2025-07-31T02:12:12.248316Z","shell.execute_reply.started":"2025-07-31T02:12:06.776557Z","shell.execute_reply":"2025-07-31T02:12:12.247765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"path = untar_data(URLs.ML_100k)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:12.249853Z","iopub.execute_input":"2025-07-31T02:12:12.250206Z","iopub.status.idle":"2025-07-31T02:12:13.018498Z","shell.execute_reply.started":"2025-07-31T02:12:12.250187Z","shell.execute_reply":"2025-07-31T02:12:13.017882Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.019164Z","iopub.execute_input":"2025-07-31T02:12:13.019345Z","iopub.status.idle":"2025-07-31T02:12:13.024194Z","shell.execute_reply.started":"2025-07-31T02:12:13.019330Z","shell.execute_reply":"2025-07-31T02:12:13.023642Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ratings = pd.read_csv(path/'u.data', delimiter='\\t', header=None,\n                      names=['user','movie','rating','timestamp'])\nratings.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.024798Z","iopub.execute_input":"2025-07-31T02:12:13.024995Z","iopub.status.idle":"2025-07-31T02:12:13.094323Z","shell.execute_reply.started":"2025-07-31T02:12:13.024979Z","shell.execute_reply":"2025-07-31T02:12:13.093559Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"movies = pd.read_csv(path/'u.item',  delimiter='|', encoding='latin-1',\n                     usecols=(0,1), names=('movie','title'), header=None)\nmovies.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.095030Z","iopub.execute_input":"2025-07-31T02:12:13.095226Z","iopub.status.idle":"2025-07-31T02:12:13.109190Z","shell.execute_reply.started":"2025-07-31T02:12:13.095211Z","shell.execute_reply":"2025-07-31T02:12:13.108502Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ratings = ratings.merge(movies)\nratings.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.110048Z","iopub.execute_input":"2025-07-31T02:12:13.110734Z","iopub.status.idle":"2025-07-31T02:12:13.143369Z","shell.execute_reply.started":"2025-07-31T02:12:13.110710Z","shell.execute_reply":"2025-07-31T02:12:13.142807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dls = CollabDataLoaders.from_df(ratings, item_name='title', bs=64)\ndls.show_batch()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.144008Z","iopub.execute_input":"2025-07-31T02:12:13.144194Z","iopub.status.idle":"2025-07-31T02:12:13.389415Z","shell.execute_reply.started":"2025-07-31T02:12:13.144179Z","shell.execute_reply":"2025-07-31T02:12:13.388658Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"n_users  = len(dls.classes['user'])\nn_movies = len(dls.classes['title'])\nn_factors = 5\n\nuser_factors = torch.randn(n_users, n_factors)\nmovie_factors = torch.randn(n_movies, n_factors)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.391563Z","iopub.execute_input":"2025-07-31T02:12:13.391809Z","iopub.status.idle":"2025-07-31T02:12:13.399327Z","shell.execute_reply.started":"2025-07-31T02:12:13.391792Z","shell.execute_reply":"2025-07-31T02:12:13.398367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class T(Module):\n    def __init__(self): \n        self.a = torch.ones(3)\n\nL(T().parameters())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.400208Z","iopub.execute_input":"2025-07-31T02:12:13.400451Z","iopub.status.idle":"2025-07-31T02:12:13.415299Z","shell.execute_reply.started":"2025-07-31T02:12:13.400424Z","shell.execute_reply":"2025-07-31T02:12:13.414674Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class T(Module):\n    def __init__(self): \n        self.a = nn.Parameter(torch.ones(3))\n\nL(T().parameters())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.416119Z","iopub.execute_input":"2025-07-31T02:12:13.417029Z","iopub.status.idle":"2025-07-31T02:12:13.464487Z","shell.execute_reply.started":"2025-07-31T02:12:13.417003Z","shell.execute_reply":"2025-07-31T02:12:13.463923Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# to view docs without source code\n?nn.Parameter\n# or\nnn.Parameter?","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.465211Z","iopub.execute_input":"2025-07-31T02:12:13.465485Z","iopub.status.idle":"2025-07-31T02:12:13.503413Z","shell.execute_reply.started":"2025-07-31T02:12:13.465458Z","shell.execute_reply":"2025-07-31T02:12:13.502628Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# to view docs WITH source code\n??nn.Parameter\n# or\nnn.Parameter??","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.504352Z","iopub.execute_input":"2025-07-31T02:12:13.504555Z","iopub.status.idle":"2025-07-31T02:12:13.533547Z","shell.execute_reply.started":"2025-07-31T02:12:13.504539Z","shell.execute_reply":"2025-07-31T02:12:13.532956Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class T(Module):\n    def __init__(self): \n        # NOTE! bias=False. Try bias=True and see what happens\n        self.a = nn.Linear(1, 3, bias=False)\n        # NOTE! Try bias=True and see what happens\n#         self.a = nn.Linear(1, 3, bias=True)\n\nt = T()\nL(t.parameters())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.534139Z","iopub.execute_input":"2025-07-31T02:12:13.534319Z","iopub.status.idle":"2025-07-31T02:12:13.541430Z","shell.execute_reply.started":"2025-07-31T02:12:13.534304Z","shell.execute_reply":"2025-07-31T02:12:13.540761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"type(t.a.weight)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.542199Z","iopub.execute_input":"2025-07-31T02:12:13.542608Z","iopub.status.idle":"2025-07-31T02:12:13.556914Z","shell.execute_reply.started":"2025-07-31T02:12:13.542564Z","shell.execute_reply":"2025-07-31T02:12:13.556214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_params(size):\n    return nn.Parameter(torch.zeros(*size).normal_(0, 0.01))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.557669Z","iopub.execute_input":"2025-07-31T02:12:13.557915Z","iopub.status.idle":"2025-07-31T02:12:13.573037Z","shell.execute_reply.started":"2025-07-31T02:12:13.557894Z","shell.execute_reply":"2025-07-31T02:12:13.572331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DotProductBias(Module):\n    def __init__(self, n_users, n_movies, n_factors, y_range=(0,5.5)):\n        self.user_factors = create_params([n_users, n_factors])\n        self.user_bias = create_params([n_users])\n        self.movie_factors = create_params([n_movies, n_factors])\n        self.movie_bias = create_params([n_movies])\n        self.y_range = y_range\n        \n    def forward(self, x):\n        users = self.user_factors[x[:,0]]\n        movies = self.movie_factors[x[:,1]]\n        res = (users*movies).sum(dim=1)\n        res += self.user_bias[x[:,0]] + self.movie_bias[x[:,1]]\n        return sigmoid_range(res, *self.y_range)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.573746Z","iopub.execute_input":"2025-07-31T02:12:13.574015Z","iopub.status.idle":"2025-07-31T02:12:13.589544Z","shell.execute_reply.started":"2025-07-31T02:12:13.573994Z","shell.execute_reply":"2025-07-31T02:12:13.588807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"n_factors = 50\n# n_factors = 3  # Try this\nmodel = DotProductBias(n_users, n_movies, n_factors=n_factors)\n# model = DotProductBias(n_users, n_movies, 50)\nlearn = Learner(dls, model, loss_func=MSELossFlat())\nlearn.fit_one_cycle(5, 5e-3, wd=0.1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:13.590433Z","iopub.execute_input":"2025-07-31T02:12:13.590715Z","iopub.status.idle":"2025-07-31T02:12:51.634282Z","shell.execute_reply.started":"2025-07-31T02:12:13.590690Z","shell.execute_reply":"2025-07-31T02:12:51.633509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.movie_bias","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:51.635163Z","iopub.execute_input":"2025-07-31T02:12:51.635991Z","iopub.status.idle":"2025-07-31T02:12:51.813526Z","shell.execute_reply.started":"2025-07-31T02:12:51.635965Z","shell.execute_reply":"2025-07-31T02:12:51.812937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"movie_bias = learn.model.movie_bias.squeeze()\nidxs = movie_bias.argsort()[:5]\n[dls.classes['title'][i] for i in idxs]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:51.814200Z","iopub.execute_input":"2025-07-31T02:12:51.814380Z","iopub.status.idle":"2025-07-31T02:12:51.864358Z","shell.execute_reply.started":"2025-07-31T02:12:51.814366Z","shell.execute_reply":"2025-07-31T02:12:51.863729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"idxs = movie_bias.argsort(descending=True)[:5]\n[dls.classes['title'][i] for i in idxs]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:51.865102Z","iopub.execute_input":"2025-07-31T02:12:51.865430Z","iopub.status.idle":"2025-07-31T02:12:51.871281Z","shell.execute_reply.started":"2025-07-31T02:12:51.865404Z","shell.execute_reply":"2025-07-31T02:12:51.870748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"g = ratings.groupby('title')['rating'].count()\ntop_movies = g.sort_values(ascending=False).index.values[:1000]\ntop_idxs = tensor([learn.dls.classes['title'].o2i[m] for m in top_movies])\nmovie_w = learn.model.movie_factors[top_idxs].cpu().detach()\nmovie_pca = movie_w.pca(3)\nfac0,fac1,fac2 = movie_pca.t()\nidxs = list(range(50))\nX = fac0[idxs]\nY = fac2[idxs]\nplt.figure(figsize=(12,12))\nplt.scatter(X, Y)\nfor i, x, y in zip(top_movies[idxs], X, Y):\n    plt.text(x,y,i, color=np.random.rand(3)*0.7, fontsize=11)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:51.871969Z","iopub.execute_input":"2025-07-31T02:12:51.872209Z","iopub.status.idle":"2025-07-31T02:12:52.428216Z","shell.execute_reply.started":"2025-07-31T02:12:51.872186Z","shell.execute_reply":"2025-07-31T02:12:52.427422Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn = collab_learner(dls, n_factors=50, y_range=(0, 5.5))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:52.429064Z","iopub.execute_input":"2025-07-31T02:12:52.429290Z","iopub.status.idle":"2025-07-31T02:12:52.438844Z","shell.execute_reply.started":"2025-07-31T02:12:52.429261Z","shell.execute_reply":"2025-07-31T02:12:52.438242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.lr_find()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:52.439553Z","iopub.execute_input":"2025-07-31T02:12:52.440068Z","iopub.status.idle":"2025-07-31T02:12:53.481758Z","shell.execute_reply.started":"2025-07-31T02:12:52.440050Z","shell.execute_reply":"2025-07-31T02:12:53.481099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.fit_one_cycle(5, 5e-3, wd=0.1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:12:53.482524Z","iopub.execute_input":"2025-07-31T02:12:53.482786Z","iopub.status.idle":"2025-07-31T02:13:29.186215Z","shell.execute_reply.started":"2025-07-31T02:12:53.482768Z","shell.execute_reply":"2025-07-31T02:13:29.185630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.186923Z","iopub.execute_input":"2025-07-31T02:13:29.187107Z","iopub.status.idle":"2025-07-31T02:13:29.191766Z","shell.execute_reply.started":"2025-07-31T02:13:29.187091Z","shell.execute_reply":"2025-07-31T02:13:29.191204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"movie_bias = learn.model.i_bias.weight.squeeze()\nidxs = movie_bias.argsort(descending=True)[:5]\n[dls.classes['title'][i] for i in idxs]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.194697Z","iopub.execute_input":"2025-07-31T02:13:29.194897Z","iopub.status.idle":"2025-07-31T02:13:29.213100Z","shell.execute_reply.started":"2025-07-31T02:13:29.194876Z","shell.execute_reply":"2025-07-31T02:13:29.212536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"collab_learner??","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.213803Z","iopub.execute_input":"2025-07-31T02:13:29.214003Z","iopub.status.idle":"2025-07-31T02:13:29.236350Z","shell.execute_reply.started":"2025-07-31T02:13:29.213987Z","shell.execute_reply":"2025-07-31T02:13:29.235624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EmbeddingDotBias??","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.237214Z","iopub.execute_input":"2025-07-31T02:13:29.237443Z","iopub.status.idle":"2025-07-31T02:13:29.256921Z","shell.execute_reply.started":"2025-07-31T02:13:29.237426Z","shell.execute_reply":"2025-07-31T02:13:29.256317Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"movie_factors = learn.model.i_weight.weight\nidx = dls.classes['title'].o2i['Silence of the Lambs, The (1991)']\ndistances = nn.CosineSimilarity(dim=1)(movie_factors, movie_factors[idx][None])\nidx = distances.argsort(descending=True)[1]\ndls.classes['title'][idx]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.257495Z","iopub.execute_input":"2025-07-31T02:13:29.257746Z","iopub.status.idle":"2025-07-31T02:13:29.362441Z","shell.execute_reply.started":"2025-07-31T02:13:29.257721Z","shell.execute_reply":"2025-07-31T02:13:29.361826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embs = get_emb_sz(dls)\nembs","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.363127Z","iopub.execute_input":"2025-07-31T02:13:29.363325Z","iopub.status.idle":"2025-07-31T02:13:29.368004Z","shell.execute_reply.started":"2025-07-31T02:13:29.363309Z","shell.execute_reply":"2025-07-31T02:13:29.367391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CollabNN(Module):\n    def __init__(self, user_sz, item_sz, y_range=(0,5.5), n_act=100):\n        self.user_factors = Embedding(*user_sz)\n        self.item_factors = Embedding(*item_sz)\n        self.layers = nn.Sequential(\n            nn.Linear(user_sz[1]+item_sz[1], n_act),\n            nn.ReLU(),\n            nn.Linear(n_act, 1))\n        self.y_range = y_range\n        \n    def forward(self, x):\n        embs = self.user_factors(x[:,0]),self.item_factors(x[:,1])\n        x = self.layers(torch.cat(embs, dim=1))\n        return sigmoid_range(x, *self.y_range)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.368551Z","iopub.execute_input":"2025-07-31T02:13:29.368761Z","iopub.status.idle":"2025-07-31T02:13:29.383615Z","shell.execute_reply.started":"2025-07-31T02:13:29.368747Z","shell.execute_reply":"2025-07-31T02:13:29.382957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CollabNN(*embs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.384355Z","iopub.execute_input":"2025-07-31T02:13:29.384617Z","iopub.status.idle":"2025-07-31T02:13:29.405932Z","shell.execute_reply.started":"2025-07-31T02:13:29.384595Z","shell.execute_reply":"2025-07-31T02:13:29.405208Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn = Learner(dls, model, loss_func=MSELossFlat())\nlearn.fit_one_cycle(5, 5e-3, wd=0.01)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:13:29.406553Z","iopub.execute_input":"2025-07-31T02:13:29.406749Z","iopub.status.idle":"2025-07-31T02:14:09.374848Z","shell.execute_reply.started":"2025-07-31T02:13:29.406734Z","shell.execute_reply":"2025-07-31T02:14:09.374103Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn = collab_learner(dls, use_nn=True, y_range=(0, 5.5), layers=[100,50])\nlearn.fit_one_cycle(5, 5e-3, wd=0.1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:14:09.375711Z","iopub.execute_input":"2025-07-31T02:14:09.375974Z","iopub.status.idle":"2025-07-31T02:14:55.505543Z","shell.execute_reply.started":"2025-07-31T02:14:09.375944Z","shell.execute_reply":"2025-07-31T02:14:55.504999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@delegates(TabularModel)\nclass EmbeddingNN(TabularModel):\n    def __init__(self, emb_szs, layers, **kwargs):\n        super().__init__(emb_szs, layers=layers, n_cont=0, out_sz=1, **kwargs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-31T02:14:55.506141Z","iopub.execute_input":"2025-07-31T02:14:55.506335Z","iopub.status.idle":"2025-07-31T02:14:55.510687Z","shell.execute_reply.started":"2025-07-31T02:14:55.506319Z","shell.execute_reply":"2025-07-31T02:14:55.510075Z"}},"outputs":[],"execution_count":null}]}