├── .gitignore ├── LICENSE ├── README.md ├── als.ipynb ├── als.py ├── train_result.png └── week18_deeplearning_cv └── ch18_8_image_search_part_3.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hyeong Jun Kim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ALS_implementation 2 | Implementation of ALS algorithm from "Collaborative Filtering for Implicit Feedback Data" [[paper]](http://yifanhu.net/PUB/cf.pdf) 3 | 4 | ## Contents 5 | - als.ipynb : notebook which contains step by step comments and visulization. 6 | - als.py : simplified python implementation 7 | 8 | ## Parameters to fix 9 | - r_lambda = 40 10 | - nf = 200 11 | - alpha = 40 12 | 13 | ## Train Result 14 | 15 | 16 | ## More detail 17 | - als.ipynb contains details of each algorithm steps. 18 | - more information can be found in my blog posting. (in Korean) [[blog]](https://yeomko.tistory.com/8) 19 | 20 | -------------------------------------------------------------------------------- /als.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## ALS Implementation\n", 8 | "- This notebook |is implementation of ALS algorithm from \"collaborative filtering for implicit dataset\"\n", 9 | "\n", 10 | "### Initialize parameters\n", 11 | "- r_lambda: normalization parameter \n", 12 | "- alpha: confidence level \n", 13 | "- nf: dimension of latent vector of each user and item \n", 14 | "- initilzed values(40, 200, 40) are the best parameters from the paper" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 98, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "r_lambda = 40\n", 24 | "nf = 200\n", 25 | "alpha = 40" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "### Initialize original rating matrix data\n", 33 | "- make sample (10 x 11) matrix\n", 34 | "- 10 : num of users\n", 35 | "- 11 : num of items" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 99, 41 | "metadata": {}, 42 | "outputs": [ 43 | { 44 | "name": "stdout", 45 | "output_type": "stream", 46 | "text": [ 47 | "(10, 11)\n" 48 | ] 49 | } 50 | ], 51 | "source": [ 52 | "import numpy as np\n", 53 | "\n", 54 | "\n", 55 | "# sample rating matrix\n", 56 | "R = np.array([[0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0],\n", 57 | " [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],\n", 58 | " [0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0],\n", 59 | " [0, 3, 4, 0, 3, 0, 0, 2, 2, 0, 0],\n", 60 | " [0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0],\n", 61 | " [0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0],\n", 62 | " [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5],\n", 63 | " [0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4],\n", 64 | " [0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0],\n", 65 | " [0, 0, 0, 3, 0, 0, 0, 0, 4, 5, 0]])\n", 66 | "print(R.shape)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "### Initialize user and item latent factor matrix\n", 74 | "- nu: num of users (10)\n", 75 | "- ni: num of items (11)\n", 76 | "- nf: dimension of latent vector" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 100, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "[[0.00457303 0.00424385 0.00616191 ... 0.0055276 0.00441167 0.0032725 ]\n", 89 | " [0.00197897 0.00110453 0.00558329 ... 0.00915551 0.00749223 0.006756 ]\n", 90 | " [0.00549164 0.00358061 0.00012187 ... 0.00723139 0.00441681 0.00632714]\n", 91 | " ...\n", 92 | " [0.00380493 0.005744 0.0024226 ... 0.00737873 0.00131759 0.00736437]\n", 93 | " [0.00115033 0.00066236 0.00046558 ... 0.00804704 0.00986983 0.00343679]\n", 94 | " [0.00504484 0.00616501 0.00130519 ... 0.00236188 0.00136329 0.00666413]]\n" 95 | ] 96 | } 97 | ], 98 | "source": [ 99 | "nu = R.shape[0]\n", 100 | "ni = R.shape[1]\n", 101 | "\n", 102 | "# initialize X and Y with very small values\n", 103 | "X = np.random.rand(nu, nf) * 0.01\n", 104 | "Y = np.random.rand(ni, nf) * 0.01\n", 105 | "\n", 106 | "print(X)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "### Initialize Binary Rating Matrix P\n", 114 | "- Convert original rating matrix R into P\n", 115 | "- Pui = 1 if Rui > 0\n", 116 | "- Pui = 0 if Rui = 0" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 101, 122 | "metadata": {}, 123 | "outputs": [ 124 | { 125 | "name": "stdout", 126 | "output_type": "stream", 127 | "text": [ 128 | "[[0 0 0 1 1 0 0 0 0 0 0]\n", 129 | " [0 0 0 0 0 0 0 0 0 0 1]\n", 130 | " [0 0 0 0 0 0 0 1 0 1 0]\n", 131 | " [0 1 1 0 1 0 0 1 1 0 0]\n", 132 | " [0 1 1 0 0 0 0 0 0 0 0]\n", 133 | " [0 0 0 0 0 0 1 0 0 1 0]\n", 134 | " [0 0 1 0 0 0 0 0 0 0 1]\n", 135 | " [0 0 0 0 0 1 0 0 0 0 1]\n", 136 | " [0 0 0 0 0 0 1 0 0 1 0]\n", 137 | " [0 0 0 1 0 0 0 0 1 1 0]]\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | "P = np.copy(R)\n", 143 | "P[P > 0] = 1\n", 144 | "print(P)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "### Initialize Confidence Matrix C\n", 152 | "- Cui = 1 + alpha * Rui\n", 153 | "- Cui means confidence level of certain rating data" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 102, 159 | "metadata": {}, 160 | "outputs": [ 161 | { 162 | "name": "stdout", 163 | "output_type": "stream", 164 | "text": [ 165 | "[[ 1 1 1 161 161 1 1 1 1 1 1]\n", 166 | " [ 1 1 1 1 1 1 1 1 1 1 41]\n", 167 | " [ 1 1 1 1 1 1 1 41 1 161 1]\n", 168 | " [ 1 121 161 1 121 1 1 81 81 1 1]\n", 169 | " [ 1 201 201 1 1 1 1 1 1 1 1]\n", 170 | " [ 1 1 1 1 1 1 201 1 1 201 1]\n", 171 | " [ 1 1 161 1 1 1 1 1 1 1 201]\n", 172 | " [ 1 1 1 1 1 161 1 1 1 1 161]\n", 173 | " [ 1 1 1 1 1 1 201 1 1 201 1]\n", 174 | " [ 1 1 1 121 1 1 1 1 161 201 1]]\n" 175 | ] 176 | } 177 | ], 178 | "source": [ 179 | "C = 1 + alpha * R\n", 180 | "print(C)" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "### Set up loss function\n", 188 | "- C: confidence matrix\n", 189 | "- P: binary rating matrix\n", 190 | "- X: user latent matrix\n", 191 | "- Y: item latent matrix\n", 192 | "- r_lambda: regularization lambda\n", 193 | "- xTy: predict matrix \n", 194 | " \n", 195 | "- Total_loss = (confidence_level * predict loss) + regularization loss" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 103, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "def loss_function(C, P, xTy, X, Y, r_lambda):\n", 205 | " predict_error = np.square(P - xTy)\n", 206 | " confidence_error = np.sum(C * predict_error)\n", 207 | " regularization = r_lambda * (np.sum(np.square(X)) + np.sum(np.square(Y)))\n", 208 | " total_loss = confidence_error + regularization\n", 209 | " return np.sum(predict_error), confidence_error, regularization, total_loss" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "### Optimization Function for user and item\n", 217 | "- X[u] = (yTCuy + lambda*I)^-1yTCuy\n", 218 | "- Y[i] = (xTCix + lambda*I)^-1xTCix\n", 219 | "- two formula is the same when it changes X to Y and u to i" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 104, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "def optimize_user(X, Y, C, P, nu, nf, r_lambda):\n", 229 | " yT = np.transpose(Y)\n", 230 | " for u in range(nu):\n", 231 | " Cu = np.diag(C[u])\n", 232 | " yT_Cu_y = np.matmul(np.matmul(yT, Cu), Y)\n", 233 | " lI = np.dot(r_lambda, np.identity(nf))\n", 234 | " yT_Cu_pu = np.matmul(np.matmul(yT, Cu), P[u])\n", 235 | " X[u] = np.linalg.solve(yT_Cu_y + lI, yT_Cu_pu)\n", 236 | "\n", 237 | "def optimize_item(X, Y, C, P, ni, nf, r_lambda):\n", 238 | " xT = np.transpose(X)\n", 239 | " for i in range(ni):\n", 240 | " Ci = np.diag(C[:, i])\n", 241 | " xT_Ci_x = np.matmul(np.matmul(xT, Ci), X)\n", 242 | " lI = np.dot(r_lambda, np.identity(nf))\n", 243 | " xT_Ci_pi = np.matmul(np.matmul(xT, Ci), P[:, i])\n", 244 | " Y[i] = np.linalg.solve(xT_Ci_x + lI, xT_Ci_pi)" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "### Train\n", 252 | "- usually ALS algorithm repeat train steps for 10 ~ 15 times" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 105, 258 | "metadata": {}, 259 | "outputs": [ 260 | { 261 | "name": "stdout", 262 | "output_type": "stream", 263 | "text": [ 264 | "----------------step 0----------------\n", 265 | "predict error: 22.767452\n", 266 | "confidence error: 3466.937317\n", 267 | "regularization: 5.636577\n", 268 | "total loss: 3472.573894\n", 269 | "----------------step 1----------------\n", 270 | "predict error: 31.023025\n", 271 | "confidence error: 296.327809\n", 272 | "regularization: 641.240713\n", 273 | "total loss: 937.568522\n", 274 | "----------------step 2----------------\n", 275 | "predict error: 34.018641\n", 276 | "confidence error: 139.069458\n", 277 | "regularization: 651.405197\n", 278 | "total loss: 790.474655\n", 279 | "----------------step 3----------------\n", 280 | "predict error: 32.074433\n", 281 | "confidence error: 120.069403\n", 282 | "regularization: 650.898109\n", 283 | "total loss: 770.967512\n", 284 | "----------------step 4----------------\n", 285 | "predict error: 29.512463\n", 286 | "confidence error: 109.299027\n", 287 | "regularization: 653.525673\n", 288 | "total loss: 762.824700\n", 289 | "----------------step 5----------------\n", 290 | "predict error: 27.060229\n", 291 | "confidence error: 102.230607\n", 292 | "regularization: 656.474772\n", 293 | "total loss: 758.705379\n", 294 | "----------------step 6----------------\n", 295 | "predict error: 25.016805\n", 296 | "confidence error: 97.437625\n", 297 | "regularization: 658.928086\n", 298 | "total loss: 756.365710\n", 299 | "----------------step 7----------------\n", 300 | "predict error: 23.442905\n", 301 | "confidence error: 94.190686\n", 302 | "regularization: 660.770057\n", 303 | "total loss: 754.960743\n", 304 | "----------------step 8----------------\n", 305 | "predict error: 22.284994\n", 306 | "confidence error: 92.007313\n", 307 | "regularization: 662.092210\n", 308 | "total loss: 754.099523\n", 309 | "----------------step 9----------------\n", 310 | "predict error: 21.454691\n", 311 | "confidence error: 90.546515\n", 312 | "regularization: 663.021474\n", 313 | "total loss: 753.567989\n", 314 | "----------------step 10----------------\n", 315 | "predict error: 20.867422\n", 316 | "confidence error: 89.570690\n", 317 | "regularization: 663.667698\n", 318 | "total loss: 753.238388\n", 319 | "----------------step 11----------------\n", 320 | "predict error: 20.455195\n", 321 | "confidence error: 88.918571\n", 322 | "regularization: 664.113910\n", 323 | "total loss: 753.032480\n", 324 | "----------------step 12----------------\n", 325 | "predict error: 20.167422\n", 326 | "confidence error: 88.482663\n", 327 | "regularization: 664.419671\n", 328 | "total loss: 752.902334\n", 329 | "----------------step 13----------------\n", 330 | "predict error: 19.967775\n", 331 | "confidence error: 88.191857\n", 332 | "regularization: 664.626891\n", 333 | "total loss: 752.818748\n", 334 | "----------------step 14----------------\n", 335 | "predict error: 19.830538\n", 336 | "confidence error: 87.999084\n", 337 | "regularization: 664.764920\n", 338 | "total loss: 752.764004\n", 339 | "final predict\n", 340 | "[array([[ 0. , 0.7213401 , 0.68235019, 0.8576162 , 0.83827723,\n", 341 | " 0.09239648, 0.58977576, 0.68720774, 0.81166324, 0.81802531,\n", 342 | " 0.30765598],\n", 343 | " [ 0. , 0.3361484 , 0.41909192, 0.10608421, 0.15560257,\n", 344 | " 0.39303202, -0.02675532, 0.14828855, 0.13284078, 0.03463808,\n", 345 | " 0.4837343 ],\n", 346 | " [ 0. , 0.46243844, 0.42946834, 0.63028311, 0.58534636,\n", 347 | " 0.03177001, 0.7517364 , 0.56616991, 0.62686475, 0.86784737,\n", 348 | " 0.16049371],\n", 349 | " [ 0. , 0.92285236, 0.94187576, 0.89650102, 0.90838244,\n", 350 | " 0.3563045 , 0.60756749, 0.77780712, 0.87870053, 0.86042895,\n", 351 | " 0.62460443],\n", 352 | " [ 0. , 0.84981068, 0.92676941, 0.62501363, 0.68101786,\n", 353 | " 0.53344484, 0.26330552, 0.57464173, 0.62942323, 0.4788263 ,\n", 354 | " 0.77920821],\n", 355 | " [ 0. , 0.29236108, 0.24197338, 0.54119403, 0.46961086,\n", 356 | " -0.08184888, 0.88182444, 0.51154855, 0.55064509, 0.93896817,\n", 357 | " -0.00732 ],\n", 358 | " [ 0. , 0.74376899, 0.8749514 , 0.37604512, 0.45811821,\n", 359 | " 0.68817876, 0.07352049, 0.40765911, 0.41042402, 0.2330001 ,\n", 360 | " 0.89526845],\n", 361 | " [ 0. , 0.56340473, 0.73794323, 0.08895129, 0.18834466,\n", 362 | " 0.78203442, -0.1124117 , 0.20273044, 0.15151988, -0.02678813,\n", 363 | " 0.92869513],\n", 364 | " [ 0. , 0.29236108, 0.24197338, 0.54119403, 0.46961086,\n", 365 | " -0.08184888, 0.88182444, 0.51154855, 0.55064509, 0.93896817,\n", 366 | " -0.00732 ],\n", 367 | " [ 0. , 0.68684109, 0.6415058 , 0.86952565, 0.83246496,\n", 368 | " 0.05966073, 0.75878686, 0.72385157, 0.83634861, 0.96332154,\n", 369 | " 0.26036486]])]\n" 370 | ] 371 | } 372 | ], 373 | "source": [ 374 | "predict_errors = []\n", 375 | "confidence_errors = []\n", 376 | "regularization_list = []\n", 377 | "total_losses = []\n", 378 | "\n", 379 | "for i in range(15):\n", 380 | " if i!=0: \n", 381 | " optimize_user(X, Y, C, P, nu, nf, r_lambda)\n", 382 | " optimize_item(X, Y, C, P, ni, nf, r_lambda)\n", 383 | " predict = np.matmul(X, np.transpose(Y))\n", 384 | " predict_error, confidence_error, regularization, total_loss = loss_function(C, P, predict, X, Y, r_lambda)\n", 385 | " \n", 386 | " predict_errors.append(predict_error)\n", 387 | " confidence_errors.append(confidence_error)\n", 388 | " regularization_list.append(regularization)\n", 389 | " total_losses.append(total_loss)\n", 390 | " \n", 391 | " print('----------------step %d----------------' % i)\n", 392 | " print(\"predict error: %f\" % predict_error)\n", 393 | " print(\"confidence error: %f\" % confidence_error)\n", 394 | " print(\"regularization: %f\" % regularization)\n", 395 | " print(\"total loss: %f\" % total_loss)\n", 396 | " \n", 397 | "predict = np.matmul(X, np.transpose(Y))\n", 398 | "print('final predict')\n", 399 | "print([predict])" 400 | ] 401 | }, 402 | { 403 | "cell_type": "code", 404 | "execution_count": 106, 405 | "metadata": {}, 406 | "outputs": [ 407 | { 408 | "data": { 409 | "text/plain": [ 410 | "
" 411 | ] 412 | }, 413 | "metadata": {}, 414 | "output_type": "display_data" 415 | }, 416 | { 417 | "data": { 418 | "image/png": "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\n", 419 | "text/plain": [ 420 | "
" 421 | ] 422 | }, 423 | "metadata": { 424 | "needs_background": "light" 425 | }, 426 | "output_type": "display_data" 427 | } 428 | ], 429 | "source": [ 430 | "from matplotlib import pyplot as plt\n", 431 | "%matplotlib inline\n", 432 | "\n", 433 | "plt.subplots_adjust(wspace=100.0, hspace=20.0)\n", 434 | "fig = plt.figure()\n", 435 | "fig.set_figheight(10)\n", 436 | "fig.set_figwidth(10)\n", 437 | "predict_error_line = fig.add_subplot(2, 2, 1)\n", 438 | "confidence_error_line = fig.add_subplot(2, 2, 2)\n", 439 | "regularization_error_line = fig.add_subplot(2, 2, 3)\n", 440 | "total_loss_line = fig.add_subplot(2, 2, 4)\n", 441 | "\n", 442 | "predict_error_line.set_title(\"Predict Error\") \n", 443 | "predict_error_line.plot(predict_errors)\n", 444 | "\n", 445 | "confidence_error_line.set_title(\"Confidence Error\")\n", 446 | "confidence_error_line.plot(confidence_errors)\n", 447 | "\n", 448 | "regularization_error_line.set_title(\"Regularization\")\n", 449 | "regularization_error_line.plot(regularization_list)\n", 450 | "\n", 451 | "total_loss_line.set_title(\"Total Loss\")\n", 452 | "total_loss_line.plot(total_losses)\n", 453 | "plt.show()" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": null, 459 | "metadata": {}, 460 | "outputs": [], 461 | "source": [] 462 | } 463 | ], 464 | "metadata": { 465 | "kernelspec": { 466 | "display_name": "Python 3", 467 | "language": "python", 468 | "name": "python3" 469 | }, 470 | "language_info": { 471 | "codemirror_mode": { 472 | "name": "ipython", 473 | "version": 3 474 | }, 475 | "file_extension": ".py", 476 | "mimetype": "text/x-python", 477 | "name": "python", 478 | "nbconvert_exporter": "python", 479 | "pygments_lexer": "ipython3", 480 | "version": "3.7.1" 481 | } 482 | }, 483 | "nbformat": 4, 484 | "nbformat_minor": 2 485 | } 486 | -------------------------------------------------------------------------------- /als.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import pyplot as plt 3 | 4 | 5 | def loss_function(C, P, xTy, X, Y, r_lambda): 6 | predict_error = np.square(P - xTy) 7 | confidence_error = np.sum(C * predict_error) 8 | regularization = r_lambda * (np.sum(np.square(X)) + np.sum(np.square(Y))) 9 | total_loss = confidence_error + regularization 10 | return np.sum(predict_error), confidence_error, regularization, total_loss 11 | 12 | 13 | def optimize_user(X, Y, C, P, nu, nf, r_lambda): 14 | yT = np.transpose(Y) 15 | for u in range(nu): 16 | Cu = np.diag(C[u]) 17 | yT_Cu_y = np.matmul(np.matmul(yT, Cu), Y) 18 | lI = np.dot(r_lambda, np.identity(nf)) 19 | yT_Cu_pu = np.matmul(np.matmul(yT, Cu), P[u]) 20 | X[u] = np.linalg.solve(yT_Cu_y + lI, yT_Cu_pu) 21 | 22 | 23 | def optimize_item(X, Y, C, P, ni, nf, r_lambda): 24 | xT = np.transpose(X) 25 | for i in range(ni): 26 | Ci = np.diag(C[:, i]) 27 | xT_Ci_x = np.matmul(np.matmul(xT, Ci), X) 28 | lI = np.dot(r_lambda, np.identity(nf)) 29 | xT_Ci_pi = np.matmul(np.matmul(xT, Ci), P[:, i]) 30 | Y[i] = np.linalg.solve(xT_Ci_x + lI, xT_Ci_pi) 31 | 32 | 33 | def plot_losses(predict_errors, confidence_errors, regularization_list, total_losses): 34 | plt.subplots_adjust(wspace=100.0, hspace=20.0) 35 | fig = plt.figure() 36 | fig.set_figheight(10) 37 | fig.set_figwidth(10) 38 | 39 | predict_error_line = fig.add_subplot(2, 2, 1) 40 | confidence_error_line = fig.add_subplot(2, 2, 2) 41 | regularization_error_line = fig.add_subplot(2, 2, 3) 42 | total_loss_line = fig.add_subplot(2, 2, 4) 43 | 44 | predict_error_line.set_title("Predict Error") 45 | predict_error_line.plot(predict_errors) 46 | 47 | confidence_error_line.set_title("Confidence Error") 48 | confidence_error_line.plot(confidence_errors) 49 | 50 | regularization_error_line.set_title("Regularization") 51 | regularization_error_line.plot(regularization_list) 52 | 53 | total_loss_line.set_title("Total Loss") 54 | total_loss_line.plot(total_losses) 55 | plt.show() 56 | 57 | 58 | def train(): 59 | predict_errors = [] 60 | confidence_errors = [] 61 | regularization_list = [] 62 | total_losses = [] 63 | 64 | for i in range(15): 65 | if i != 0: 66 | optimize_user(X, Y, C, P, nu, nf, r_lambda) 67 | optimize_item(X, Y, C, P, ni, nf, r_lambda) 68 | predict = np.matmul(X, np.transpose(Y)) 69 | predict_error, confidence_error, regularization, total_loss = loss_function(C, P, predict, X, Y, r_lambda) 70 | 71 | predict_errors.append(predict_error) 72 | confidence_errors.append(confidence_error) 73 | regularization_list.append(regularization) 74 | total_losses.append(total_loss) 75 | 76 | print('----------------step %d----------------' % i) 77 | print("predict error: %f" % predict_error) 78 | print("confidence error: %f" % confidence_error) 79 | print("regularization: %f" % regularization) 80 | print("total loss: %f" % total_loss) 81 | 82 | predict = np.matmul(X, np.transpose(Y)) 83 | print('final predict') 84 | print([predict]) 85 | 86 | return predict_errors, confidence_errors, regularization_list, total_losses 87 | 88 | 89 | if __name__ == '__main__': 90 | r_lambda = 40 91 | nf = 200 92 | alpha = 40 93 | 94 | R = np.array([[0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0], 95 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 96 | [0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0], 97 | [0, 3, 4, 0, 3, 0, 0, 2, 2, 0, 0], 98 | [0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0], 99 | [0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0], 100 | [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5], 101 | [0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4], 102 | [0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0], 103 | [0, 0, 0, 3, 0, 0, 0, 0, 4, 5, 0]]) 104 | 105 | nu = R.shape[0] 106 | ni = R.shape[1] 107 | 108 | # initialize X and Y with very small values 109 | X = np.random.rand(nu, nf) * 0.01 110 | Y = np.random.rand(ni, nf) * 0.01 111 | 112 | P = np.copy(R) 113 | P[P > 0] = 1 114 | C = 1 + alpha * R 115 | 116 | predict_errors, confidence_errors, regularization_list, total_losses = train() 117 | plot_losses(predict_errors, confidence_errors, regularization_list, total_losses) 118 | -------------------------------------------------------------------------------- /train_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeomko22/ALS_implementation/b89f4ef31dd5052defc643f91f2f48b1a27b6df5/train_result.png --------------------------------------------------------------------------------