1 Project Scope

Implement a matrix factorization method—such as singular value decomposition (SVD) or Alternating Least Squares (ALS)—in the context of a recommender system.

2 Introduction

I begin this project with a discussion of Singular Value Decomposition and how it can be used in recommender systems which is then followed by a Toy example. I then implement the SVD on real data. Finally, I compare the two models and evualate their performance.

3 Singular Value Decomposition-SVD

For any matrix, M, in the set of complex number of dimensions mxn: \(\\ \) \(M \in \mathbb{C}^{mxn}\) \(\\ \)

Can be factorized into its three main component parts:\(\\ \)

\(M = U\Sigma V^{*}\) \(\\ \)

Where \(U \text{= mxm unitary matrix, } \Sigma \text{= an mxn diagonal matrix, and } V^{*} \text{= a transpose of V which is a nxn unitary matix.}\)

The dot product of these three components returns the original matrix. For recommender systems, SVD is a collaborative filtering technique. User Based Collaborative filtering techniques seeks to find relationships between users and user-rated items in a matrix in order to intelligently recommend items to other users based on these relationships.

Using the example above, “U is a m x r orthogonal left singular matrix, which represents the relationship between users and latent factors, S is a r x r diagonal matrix, which describes the strength of each latent factor and V is a r x n diagonal right singular matrix, which indicates the similarity between items and latent factors. The latent factors here are the characteristics of the items, for example, the genre of the music. The SVD decreases the dimension of the utility matrix A by extracting its latent factors. It maps each user and each item into a r-dimensional latent space. This mapping facilitates a clear representation of relationships between users and items.” Citation: SINGULAR VALUE DECOMPOSITION (SVD) & ITS APPLICATION IN RECOMMENDER SYSTEM by DR. VAIBHAV KUMAR, 3/25/2020

4 Toy Example of SVD

In the toy example below, I created a 20x10 matrix of 20 users giving ratings to 10 movies, 5 comedies and 5 dramas. This will be a dense matrix where each user rates a movie on a scale of 1 to 5. This also means that each user has seen all 20 movies.Secondly, I assigned the ratings randomly which assumes that each user does not have a particular taste for one genre over another.

Yesterday Knives Out Jo Jo Rabbit Good Boys Zombieland Joker The Irishman Marriage Story Parasite Ad Astra
2 5 2 4 2 2 5 2 4 2
4 4 3 1 4 4 4 3 1 4
3 4 3 3 3 3 4 3 3 3
5 5 2 2 4 5 5 2 2 4
5 4 2 4 1 5 4 2 4 1
1 4 2 3 3 1 4 2 3 3
3 3 2 4 5 3 3 2 4 5
5 3 3 4 5 5 3 3 4 5
3 2 2 4 5 3 2 2 4 5
3 2 4 3 2 3 2 4 3 2
5 5 1 4 2 5 5 1 4 2
3 5 3 4 4 3 5 3 4 4
4 4 4 4 2 4 4 4 4 2
3 4 1 1 4 3 4 1 1 4
1 1 3 3 2 1 1 3 3 2
5 3 2 2 2 5 3 2 2 2
2 4 2 3 4 2 4 2 3 4
1 2 4 3 1 1 2 4 3 1
2 2 5 2 3 2 2 5 2 3
5 2 2 1 3 5 2 2 1 3

Using the R function, SVD, I created the SVD matrix from A and stored each component into the variables, U, Sigma, and V

Sigma is a special matrix with zeroes except on the diagonal.

##           [,1]     [,2]     [,3]     [,4]         [,5]         [,6]
##  [1,] 8.264242 0.000000 0.000000 0.000000 0.000000e+00 0.000000e+00
##  [2,] 0.000000 6.880135 0.000000 0.000000 0.000000e+00 0.000000e+00
##  [3,] 0.000000 0.000000 6.549364 0.000000 0.000000e+00 0.000000e+00
##  [4,] 0.000000 0.000000 0.000000 4.633776 0.000000e+00 0.000000e+00
##  [5,] 0.000000 0.000000 0.000000 0.000000 2.062719e-15 0.000000e+00
##  [6,] 0.000000 0.000000 0.000000 0.000000 0.000000e+00 9.345221e-16
##  [7,] 0.000000 0.000000 0.000000 0.000000 0.000000e+00 0.000000e+00
##  [8,] 0.000000 0.000000 0.000000 0.000000 0.000000e+00 0.000000e+00
##  [9,] 0.000000 0.000000 0.000000 0.000000 0.000000e+00 0.000000e+00
## [10,] 0.000000 0.000000 0.000000 0.000000 0.000000e+00 0.000000e+00
##               [,7]         [,8]         [,9]        [,10]
##  [1,] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
##  [2,] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
##  [3,] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
##  [4,] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
##  [5,] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
##  [6,] 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
##  [7,] 4.771221e-16 0.000000e+00 0.000000e+00 0.000000e+00
##  [8,] 0.000000e+00 4.299478e-16 0.000000e+00 0.000000e+00
##  [9,] 0.000000e+00 0.000000e+00 4.638853e-17 0.000000e+00
## [10,] 0.000000e+00 0.000000e+00 0.000000e+00 4.172817e-31

U is the Left Singular Matrix

##              [,1]        [,2]         [,3]         [,4]        [,5]
##  [1,]  0.12488869  0.31615487  0.253203128 -0.145294363  0.31570695
##  [2,]  0.21158159 -0.16884147 -0.025775501  0.461069432  0.12006594
##  [3,]  0.20142369  0.21863714  0.259807608  0.223328859  0.36430385
##  [4,]  0.32693663 -0.14054134  0.077605437  0.153588875  0.19824291
##  [5,]  0.09697744 -0.12128183  0.353545274 -0.327278503  0.09113037
##  [6,]  0.12544501  0.40755375 -0.002214306  0.053939009  0.02375480
##  [7,]  0.16706288  0.10130947 -0.346017308 -0.261859794 -0.06105016
##  [8,]  0.13753108 -0.25139470 -0.275806239 -0.260190333  0.13399714
##  [9,]  0.07700449  0.01360064 -0.391696633 -0.305590914  0.24445613
## [10,] -0.33041467 -0.14623695  0.111295965  0.001648263 -0.28381272
## [11,]  0.23989723 -0.04235175  0.268534575 -0.296793679 -0.25334000
## [12,]  0.22025012  0.34242399  0.056372883  0.002916405 -0.57044012
## [13,] -0.11387204 -0.03141331  0.424789094 -0.123522223  0.09437724
## [14,]  0.33331941 -0.01378286 -0.080422408  0.228956532 -0.12010697
## [15,] -0.31357425  0.14677715 -0.141786801 -0.136872435  0.11693824
## [16,]  0.16398852 -0.33500526  0.203443906 -0.059289444 -0.24693447
## [17,]  0.23519960  0.30054092 -0.142806520  0.057680857 -0.09501954
## [18,] -0.31434841  0.16871814  0.140898427  0.049283640  0.14489816
## [19,] -0.27257591 -0.01344144 -0.101213833  0.402447609 -0.10802648
## [20,]  0.14603303 -0.39392145 -0.030107286  0.086123873  0.11736681
##               [,6]         [,7]         [,8]         [,9]        [,10]
##  [1,] -0.779445222 -0.157176215  0.100146806  0.010267283  0.002684606
##  [2,] -0.164425705  0.495312555  0.046318702  0.092314918 -0.031282677
##  [3,]  0.465057585 -0.359007646  0.505593354  0.075316635  0.013797395
##  [4,]  0.062007913 -0.459900586 -0.702456705 -0.256559362 -0.008677261
##  [5,]  0.110837562  0.274012117  0.054570018 -0.408802059 -0.672721791
##  [6,] -0.057716049  0.075863669 -0.031074502 -0.002966979 -0.033234840
##  [7,]  0.025475996 -0.147230447  0.162896994 -0.137427010  0.080831047
##  [8,] -0.105206020 -0.064795178  0.108028730  0.036475973  0.042039056
##  [9,]  0.086502979 -0.072451108  0.095848544 -0.002635708 -0.188236923
## [10,] -0.111521490 -0.331644171  0.137644685 -0.096875613 -0.093857180
## [11,]  0.052488501 -0.097568609 -0.155869829  0.772212460 -0.208009184
## [12,]  0.050442936 -0.008363157  0.009937926 -0.253690383  0.025873605
## [13,]  0.143415737  0.114860414 -0.027190109 -0.049199037  0.313682236
## [14,] -0.069772194  0.007646291  0.089203960 -0.007748251 -0.126134128
## [15,]  0.090908732 -0.027995163 -0.135769814  0.067883047 -0.062249879
## [16,] -0.126085341 -0.152957236  0.257969106 -0.177057445  0.210163791
## [17,]  0.117309008  0.100959937 -0.082451459  0.036321224 -0.140487215
## [18,]  0.074803673  0.081468140 -0.162756448  0.044456853 -0.032607753
## [19,] -0.154249090 -0.321561765  0.098155909  0.075286595 -0.511720553
## [20,]  0.002257128 -0.019061988  0.086476455  0.122752450 -0.060006421

V is the Right Singular Matrix

##             [,1]        [,2]        [,3]       [,4]       [,5]        [,6]
##  [1,]  0.1606829 -0.57792941  0.15341176 -0.1290094 -0.1862738 -0.67309897
##  [2,]  0.3509315  0.31712440  0.35872341  0.2181668 -0.2981999 -0.04035824
##  [3,] -0.5276481 -0.03413488  0.05969463  0.3418462 -0.3732062 -0.33727279
##  [4,] -0.1823604  0.24937615  0.01468802 -0.5516706 -0.3328740 -0.05210360
##  [5,]  0.1983942  0.04556374 -0.58651782  0.1206671 -0.3098544 -0.03822333
##  [6,]  0.1606829 -0.57792941  0.15341176 -0.1290094 -0.4334349  0.59665232
##  [7,]  0.3509315  0.31712440  0.35872341  0.2181668 -0.3215089 -0.03608841
##  [8,] -0.5276481 -0.03413488  0.05969463  0.3418462 -0.2465025  0.26082614
##  [9,] -0.1823604  0.24937615  0.01468802 -0.5516706 -0.2868347 -0.02434305
## [10,]  0.1983942  0.04556374 -0.58651782  0.1206671 -0.3098544 -0.03822333
##              [,7]        [,8]          [,9]         [,10]
##  [1,] -0.25549383  0.21665464 -0.0048491063 -3.653051e-15
##  [2,] -0.18678983 -0.15550006 -0.6711015892  3.344319e-15
##  [3,]  0.41360107 -0.41896880  0.0190656271 -2.889688e-15
##  [4,]  0.42125243  0.50506483 -0.2322412609  6.754800e-16
##  [5,] -0.04358234  0.02495286 -0.0026795854 -7.071068e-01
##  [6,]  0.16832915 -0.16674892 -0.0005100645  1.925451e-15
##  [7,]  0.09962515  0.20540577  0.6657424184 -5.172332e-15
##  [8,] -0.50076575  0.46887452 -0.0244247978  1.106110e-15
##  [9,] -0.50841711 -0.45515912  0.2268820902 -2.389677e-15
## [10,] -0.04358234  0.02495286 -0.0026795854  7.071068e-01

Columns of U and V are orthogonal.

##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]    1    0    0    0    0    0    0    0    0     0
##  [2,]    0    1    0    0    0    0    0    0    0     0
##  [3,]    0    0    1    0    0    0    0    0    0     0
##  [4,]    0    0    0    1    0    0    0    0    0     0
##  [5,]    0    0    0    0    1    0    0    0    0     0
##  [6,]    0    0    0    0    0    1    0    0    0     0
##  [7,]    0    0    0    0    0    0    1    0    0     0
##  [8,]    0    0    0    0    0    0    0    1    0     0
##  [9,]    0    0    0    0    0    0    0    0    1     0
## [10,]    0    0    0    0    0    0    0    0    0     1
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]    1    0    0    0    0    0    0    0    0     0
##  [2,]    0    1    0    0    0    0    0    0    0     0
##  [3,]    0    0    1    0    0    0    0    0    0     0
##  [4,]    0    0    0    1    0    0    0    0    0     0
##  [5,]    0    0    0    0    1    0    0    0    0     0
##  [6,]    0    0    0    0    0    1    0    0    0     0
##  [7,]    0    0    0    0    0    0    1    0    0     0
##  [8,]    0    0    0    0    0    0    0    1    0     0
##  [9,]    0    0    0    0    0    0    0    0    1     0
## [10,]    0    0    0    0    0    0    0    0    0     1

Taking the dot product of the three matrixes returns us back to the original matrix.

##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]   -1    1   -1    1   -1   -1    1   -1    1    -1
##  [2,]    1    1    0   -2    1    1    1    0   -2     1
##  [3,]    0    2    0    0    0    0    2    0    0     0
##  [4,]    1    1   -1   -1    0    1    1   -1   -1     0
##  [5,]    1    1   -1    1   -1    1    1   -1    1    -1
##  [6,]   -1    1   -1    0    0   -1    1   -1    0     0
##  [7,]    0    0   -1    1    1    0    0   -1    1     1
##  [8,]    1   -1   -1    0    1    1   -1   -1    0     1
##  [9,]    0   -1   -1    1    1    0   -1   -1    1     1
## [10,]    0   -1    2    0   -1    0   -1    2    0    -1
## [11,]    1    1   -1    0   -1    1    1   -1    0    -1
## [12,]   -1    2   -1    0    0   -1    2   -1    0     0
## [13,]    0    0    0    0   -2    0    0    0    0    -2
## [14,]    0    1   -1   -1    1    0    1   -1   -1     1
## [15,]   -1   -1    1    1    0   -1   -1    1    1     0
## [16,]    2    0   -1   -1   -1    2    0   -1   -1    -1
## [17,]   -1    1   -1    0    1   -1    1   -1    0     1
## [18,]   -1    0    1    1   -1   -1    0    1    1    -1
## [19,]   -1   -1    2   -1    0   -1   -1    2   -1     0
## [20,]    2    0    0   -1    0    2    0    0   -1     0

In the custom function below, I reduced the number of dimensions in order to achieve 80% of the “energy” of the original diagonal matrix Sigma.

For this example, the number of dimensions is reduced to 2.

## [1] 2

Below, we see that we can reduce the number of dimensions down to just 1 and still retain .933 of the energy of the original diagonal matrix.

## [1] 0.6424109

In the code blocks below we see the three component matrixes reduced.

##              [,1]        [,2]
##  [1,]  0.12488869  0.31615487
##  [2,]  0.21158159 -0.16884147
##  [3,]  0.20142369  0.21863714
##  [4,]  0.32693663 -0.14054134
##  [5,]  0.09697744 -0.12128183
##  [6,]  0.12544501  0.40755375
##  [7,]  0.16706288  0.10130947
##  [8,]  0.13753108 -0.25139470
##  [9,]  0.07700449  0.01360064
## [10,] -0.33041467 -0.14623695
## [11,]  0.23989723 -0.04235175
## [12,]  0.22025012  0.34242399
## [13,] -0.11387204 -0.03141331
## [14,]  0.33331941 -0.01378286
## [15,] -0.31357425  0.14677715
## [16,]  0.16398852 -0.33500526
## [17,]  0.23519960  0.30054092
## [18,] -0.31434841  0.16871814
## [19,] -0.27257591 -0.01344144
## [20,]  0.14603303 -0.39392145
##           [,1]       [,2]      [,3]       [,4]       [,5]        [,6]
## [1,] 0.1606829 -0.5779294 0.1534118 -0.1290094 -0.1862738 -0.67309897
## [2,] 0.3509315  0.3171244 0.3587234  0.2181668 -0.2981999 -0.04035824
##            [,7]       [,8]         [,9]         [,10]
## [1,] -0.2554938  0.2166546 -0.004849106 -3.653051e-15
## [2,] -0.1867898 -0.1555001 -0.671101589  3.344319e-15

4.1 User recommendation

In the code blocks below, we map two new users onto the concept space. Even though both users rated a comed(ies) higher, we do not see a similarity in terms of the two dimensional concept space between the two users.

This may be due to how the original matrix in this example was created by assigning the ratings randomly, not accounting for personal tastes of the users. Also, there’s sparsity in these two matrixes.

##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,]    5    0    0    0    0    0    1    0    1     1
##           [,1]      [,2]
## [1,] 0.5430716 0.8967659
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,]    0    4    5    4    0    0    0    0    0     1
##           [,1]     [,2]
## [1,] -2.060696 3.934782

In the next section, I applied the SVD to actual movie ratings data using the MovieLens dataset.

5 Implement Manual SVD

5.1 Import Data

I began by downing the the MovieLense rating matrix which is within the Recommenderlab R package Link

## 943 x 1664 rating matrix of class 'realRatingMatrix' with 99392 ratings.

The movie_ratings_matrix is comprised of 147 users and movies that have been watched least 116 movies.

## [1] 147 116

I put the movie_ratings_matrix through the same process as the Toy matrix from part one. The key differences are that the movie_ratings_matrix won’t be a densely populated as the Toy matrix. Also, the ratings are not assigned randomly. Here we will have real user sentiment.

## [1] 116
## [1] 147 116
## [1] 116 116

I was able to reduce the number of dimensions of the movie_ratings_matrix from 116 to 46.

## [1] 46
## [1] 0.8415052

I reduced the dimensions of the components of the main matrix.

5.2 User 35

Here, I put the SVD recommender to the task of recommending movies to user 35. First, I projected user 35’s ratings onto the movies concept space by taking the dot product of user 35’s ratings with the concept space denoted by the transpose of A1V.

I found 11 movies that the SVD recommender would recommend (>1) to user 35

##                                    Titles SVD_Ratings
## 1             Fish Called Wanda, A (1988)    2.444057
## 2                          Aladdin (1992)    2.339122
## 3                        Toy Story (1995)    2.126828
## 4  Monty Python and the Holy Grail (1974)    2.022419
## 5                          Twister (1996)    1.858616
## 6                Wizard of Oz, The (1939)    1.762824
## 7              Usual Suspects, The (1995)    1.600618
## 8             Sleepless in Seattle (1993)    1.269091
## 9            2001: A Space Odyssey (1968)    1.267198
## 10              Mr. Holland's Opus (1995)    1.256991
## 11                   Seven (Se7en) (1995)    1.148647

Of these 11 movies that SVD recommended, User 35 had rated three of them. Not rated 4, and rated another 4 negatively.

## [1] Titles        User35_Rating
## <0 rows> (or 0-length row.names)

Below are the 4 movies that the SVD recommended to User 35 that User 35 had rated very lowly.

## [1] Titles        User35_Rating
## <0 rows> (or 0-length row.names)

In summary, we can say that a manual implementation of the SVD yielded about a 27% recommendation success rate with a 36% failure rate. We can also say that of the 4 movies that the SVD recommended to User 35, s/he will like only one of them. More than likely, “Usual Suspects, The (1995)”.

In the final section, I take a more formal evaluation approach and compare the SVD model to the User Based Collaborative Filterin model.

6 SVD vs. UCBF

6.2 Evaluation of models

To evaluate the two models, I created a list of UBCF and SVD models with different parameters (pearson, cosine, and null for the SVD model). This list was fed into the evaluate function and below. The visualizations below both show that the UBCF using the “pearson” method had the larger area under the curve.

## $k
## [1] 10
## 
## $maxiter
## [1] 100
## 
## $normalize
## [1] "center"
## $method
## [1] "cosine"
## 
## $nn
## [1] 25
## 
## $sample
## [1] FALSE
## 
## $normalize
## [1] "center"
## UBCF run fold/sample [model time/prediction time]
##   1  [0.02sec/0.05sec] 
##   2  [0sec/0.06sec] 
##   3  [0sec/0.02sec] 
##   4  [0sec/0.01sec] 
##   5  [0sec/0.02sec] 
##   6  [0.02sec/0.01sec] 
##   7  [0sec/0.02sec] 
##   8  [0sec/0.02sec] 
## UBCF run fold/sample [model time/prediction time]
##   1  [0sec/0.01sec] 
##   2  [0sec/0.01sec] 
##   3  [0sec/0.02sec] 
##   4  [0sec/0.02sec] 
##   5  [0sec/0.01sec] 
##   6  [0sec/0.01sec] 
##   7  [0sec/0.02sec] 
##   8  [0sec/0.02sec] 
## SVD run fold/sample [model time/prediction time]
##   1  [0.01sec/0sec] 
##   2  [0sec/0.01sec] 
##   3  [0sec/0sec] 
##   4  [0sec/0.01sec] 
##   5  [0sec/0.01sec] 
##   6  [0.02sec/0sec] 
##   7  [0sec/0.02sec] 
##   8  [0sec/0sec]

## integer(0)

## $UBCF_cos
##             TP         FP        FN        TN precision     recall        TPR
## 1    0.7321429  0.2678571 57.107143 47.892857 0.7321429 0.01309719 0.01309719
## 5    3.6190476  1.3809524 54.220238 46.779762 0.7238095 0.06308192 0.06308192
## 10   6.9166667  3.0833333 50.922619 45.077381 0.6916667 0.12159588 0.12159588
## 20  13.1488095  6.8511905 44.690476 41.309524 0.6574405 0.23069135 0.23069135
## 30  19.0773810 10.9226190 38.761905 37.238095 0.6359127 0.33373189 0.33373189
## 40  25.0773810 14.9226190 32.761905 33.238095 0.6269345 0.43763359 0.43763359
## 50  30.6130952 19.3869048 27.226190 28.773810 0.6122619 0.53223866 0.53223866
## 60  36.0892857 23.9107143 21.750000 24.250000 0.6014881 0.62635088 0.62635088
## 70  41.6488095 28.3511905 16.190476 19.809524 0.5949830 0.72389868 0.72389868
## 80  46.4702381 33.5297619 11.369048 14.630952 0.5808780 0.80631695 0.80631695
## 90  51.0178571 38.9821429  6.821429  9.178571 0.5668651 0.88450125 0.88450125
## 100 55.3630952 44.6369048  2.476190  3.523810 0.5536310 0.95929082 0.95929082
##             FPR
## 1   0.004920696
## 5   0.025619306
## 10  0.060597975
## 20  0.138903446
## 30  0.223048226
## 40  0.305263160
## 50  0.396820943
## 60  0.489769377
## 70  0.582047823
## 80  0.690164542
## 90  0.806528233
## 100 0.926202762
## 
## $UBCF_cor
##             TP         FP        FN        TN precision     recall        TPR
## 1    0.7797619  0.2202381 57.059524 47.940476 0.7797619 0.01362783 0.01362783
## 5    3.7916667  1.2083333 54.047619 46.952381 0.7583333 0.06715826 0.06715826
## 10   7.1726190  2.8273810 50.666667 45.333333 0.7172619 0.12661884 0.12661884
## 20  13.6488095  6.3511905 44.190476 41.809524 0.6824405 0.24138699 0.24138699
## 30  19.6845238 10.3154762 38.154762 37.845238 0.6561508 0.34569971 0.34569971
## 40  25.5952381 14.4047619 32.244048 33.755952 0.6398810 0.44867168 0.44867168
## 50  31.3214286 18.6785714 26.517857 29.482143 0.6264286 0.54785881 0.54785881
## 60  36.8154762 23.1845238 21.023810 24.976190 0.6135913 0.64162625 0.64162625
## 70  42.0357143 27.9642857 15.803571 20.196429 0.6005102 0.73149773 0.73149773
## 80  46.8154762 33.1845238 11.023810 14.976190 0.5851935 0.81319244 0.81319244
## 90  51.4107143 38.5892857  6.428571  9.571429 0.5712302 0.89170461 0.89170461
## 100 55.5238095 44.4761905  2.315476  3.684524 0.5552381 0.96120372 0.96120372
##             FPR
## 1   0.004050474
## 5   0.023419391
## 10  0.055886420
## 20  0.128333107
## 30  0.208123748
## 40  0.293958117
## 50  0.383662801
## 60  0.476357845
## 70  0.574105896
## 80  0.684013372
## 90  0.797297860
## 100 0.922120660
## 
## $SVD
##             TP         FP        FN        TN precision     recall        TPR
## 1    0.7678571  0.2321429 57.071429 47.928571 0.7678571 0.01356337 0.01356337
## 5    3.6428571  1.3571429 54.196429 46.803571 0.7285714 0.06492456 0.06492456
## 10   6.9880952  3.0119048 50.851190 45.148810 0.6988095 0.12423557 0.12423557
## 20  13.0654762  6.9345238 44.773810 41.226190 0.6532738 0.22984463 0.22984463
## 30  18.9464286 11.0535714 38.892857 37.107143 0.6315476 0.33305743 0.33305743
## 40  24.6428571 15.3571429 33.196429 32.803571 0.6160714 0.43157439 0.43157439
## 50  29.9880952 20.0119048 27.851190 28.148810 0.5997619 0.52335405 0.52335405
## 60  35.2202381 24.7797619 22.619048 23.380952 0.5870040 0.61315416 0.61315416
## 70  40.4404762 29.5595238 17.398810 18.601190 0.5777211 0.70228899 0.70228899
## 80  45.4404762 34.5595238 12.398810 13.601190 0.5680060 0.78746813 0.78746813
## 90  50.4583333 39.5416667  7.380952  8.619048 0.5606481 0.87325001 0.87325001
## 100 55.0892857 44.9107143  2.750000  3.250000 0.5508929 0.95232488 0.95232488
##             FPR
## 1   0.004418973
## 5   0.027713119
## 10  0.061456056
## 20  0.141856640
## 30  0.227157736
## 40  0.314263089
## 50  0.410835990
## 60  0.510507924
## 70  0.608971849
## 80  0.714144416
## 90  0.817527646
## 100 0.931696091

7 RMSE - SVD vs UBCF

7.2 UBCF

##          RMSE       MSE       MAE
## 7   0.8172493 0.6678964 0.6736455
## 13  1.1810835 1.3949581 0.9845381
## 59  1.1551622 1.3343997 0.7479544
## 145 1.0628515 1.1296534 0.8978653
## 194 0.9384819 0.8807483 0.7610926
## 314 1.3267070 1.7601516 0.9592185
## 363 1.2236546 1.4973306 1.0022238
## 393 0.8385594 0.7031819 0.6577984
## 437 1.0567320 1.1166825 0.8219672
## 457 0.5728772 0.3281883 0.4261889
## 524 0.9582465 0.9182363 0.8226710
## 618 0.8456415 0.7151095 0.6352287
## 682 0.8131040 0.6611380 0.6452044
## 716 1.1388042 1.2968750 0.8874681
## 807 0.7694216 0.5920096 0.6513251
## 843 1.1527737 1.3288873 1.0204700
## 854 0.9375588 0.8790165 0.7359421
## 880 0.7721467 0.5962105 0.5873766
## 916 0.9253646 0.8562997 0.7636788
## 919 0.9315090 0.8677091 0.7501819
##      RMSE       MSE       MAE 
## 0.9799797 0.9603602 0.7649969
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

8 Conclusion

This project looked at the SVD model against randomly assigned user ratings, user ratings from the MovieLens dataset, and did a formal comparison between SVD and the User Based Collaborative filters. We saw that the SVD model performed poorly against randomly assigned ratings, improved somewhat against actual user ratings, and did not perform as well as the UBCF models.

LS0tDQp0aXRsZTogIkRBVEEgNjEyIFByb2plY3QgMyINCmF1dGhvcjogIkpvaG4gSy4gSGFuY29jayINCmRhdGU6ICI2LzE3LzIwMjAiDQpvdXRwdXQ6DQogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IHBhcGVyDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiBubw0KLS0tDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHJlY29tbWVuZGVybGFiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGNhVG9vbHMpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHJtZGZvcm1hdHMpDQpsaWJyYXJ5KGxzYSkNCmxpYnJhcnkoQkJtaXNjKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQoNCiMgUHJvamVjdCBTY29wZQ0KSW1wbGVtZW50IGEgbWF0cml4IGZhY3Rvcml6YXRpb24gbWV0aG9k4oCUc3VjaCBhcyBzaW5ndWxhciB2YWx1ZSBkZWNvbXBvc2l0aW9uIChTVkQpIG9yIEFsdGVybmF0aW5nIExlYXN0IFNxdWFyZXMgKEFMUynigJRpbiB0aGUgY29udGV4dCBvZiBhIHJlY29tbWVuZGVyIHN5c3RlbS4gDQoNCiMgSW50cm9kdWN0aW9uIA0KDQpJIGJlZ2luIHRoaXMgcHJvamVjdCB3aXRoIGEgZGlzY3Vzc2lvbiBvZiBTaW5ndWxhciBWYWx1ZSBEZWNvbXBvc2l0aW9uIGFuZCBob3cgaXQgY2FuIGJlIHVzZWQgaW4gcmVjb21tZW5kZXIgc3lzdGVtcyB3aGljaCBpcyB0aGVuIGZvbGxvd2VkIGJ5IGEgVG95IGV4YW1wbGUuICBJIHRoZW4gaW1wbGVtZW50IHRoZSBTVkQgb24gcmVhbCBkYXRhLiAgRmluYWxseSwgSSBjb21wYXJlIHRoZSB0d28gbW9kZWxzIGFuZCBldnVhbGF0ZSB0aGVpciBwZXJmb3JtYW5jZS4gDQoNCg0KDQojIFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24tU1ZEDQpGb3IgYW55IG1hdHJpeCwgTSwgaW4gdGhlIHNldCBvZiBjb21wbGV4IG51bWJlciBvZiBkaW1lbnNpb25zIG14bjogXChcXCBcKQ0KXCggTSBcaW4gXG1hdGhiYntDfV57bXhufSAgXCkgDQpcKFxcIFwpDQoNCkNhbiBiZSBmYWN0b3JpemVkIGludG8gaXRzIHRocmVlIG1haW4gY29tcG9uZW50IHBhcnRzOlwoXFwgXCkNCg0KXCggTSA9IFVcU2lnbWEgVl57Kn0gIFwpIFwoXFwgXCkNCg0KV2hlcmUgXChVIFx0ZXh0ez0gbXhtIHVuaXRhcnkgbWF0cml4LCB9IFxTaWdtYSBcdGV4dHs9IGFuIG14biBkaWFnb25hbCBtYXRyaXgsIGFuZCB9ICBWXnsqfSBcdGV4dHs9IGEgdHJhbnNwb3NlIG9mIFYgd2hpY2ggaXMgYSBueG4gdW5pdGFyeSBtYXRpeC59ICBcKQ0KDQoNClRoZSBkb3QgcHJvZHVjdCBvZiB0aGVzZSB0aHJlZSBjb21wb25lbnRzIHJldHVybnMgdGhlIG9yaWdpbmFsIG1hdHJpeC4gRm9yIHJlY29tbWVuZGVyIHN5c3RlbXMsIFNWRCBpcyBhIGNvbGxhYm9yYXRpdmUgZmlsdGVyaW5nIHRlY2huaXF1ZS4gVXNlciBCYXNlZCBDb2xsYWJvcmF0aXZlIGZpbHRlcmluZyB0ZWNobmlxdWVzIHNlZWtzIHRvIGZpbmQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHVzZXJzIGFuZCB1c2VyLXJhdGVkIGl0ZW1zIGluIGEgbWF0cml4IGluIG9yZGVyIHRvIGludGVsbGlnZW50bHkgcmVjb21tZW5kIGl0ZW1zIHRvIG90aGVyIHVzZXJzIGJhc2VkIG9uIHRoZXNlIHJlbGF0aW9uc2hpcHMuIA0KDQpVc2luZyB0aGUgZXhhbXBsZSBhYm92ZSwgIlUgaXMgYSBtIHggciBvcnRob2dvbmFsIGxlZnQgc2luZ3VsYXIgbWF0cml4LCB3aGljaCByZXByZXNlbnRzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB1c2VycyBhbmQgbGF0ZW50IGZhY3RvcnMsIFMgaXMgYSByIHggciBkaWFnb25hbCBtYXRyaXgsIHdoaWNoIGRlc2NyaWJlcyB0aGUgc3RyZW5ndGggb2YgZWFjaCBsYXRlbnQgZmFjdG9yIGFuZCBWIGlzIGEgciB4IG4gZGlhZ29uYWwgcmlnaHQgc2luZ3VsYXIgbWF0cml4LCB3aGljaCBpbmRpY2F0ZXMgdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiBpdGVtcyBhbmQgbGF0ZW50IGZhY3RvcnMuIFRoZSBsYXRlbnQgZmFjdG9ycyBoZXJlIGFyZSB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBpdGVtcywgZm9yIGV4YW1wbGUsIHRoZSBnZW5yZSBvZiB0aGUgbXVzaWMuIFRoZSBTVkQgZGVjcmVhc2VzIHRoZSBkaW1lbnNpb24gb2YgdGhlIHV0aWxpdHkgbWF0cml4IEEgYnkgZXh0cmFjdGluZyBpdHMgbGF0ZW50IGZhY3RvcnMuIEl0IG1hcHMgZWFjaCB1c2VyIGFuZCBlYWNoIGl0ZW0gaW50byBhIHItZGltZW5zaW9uYWwgbGF0ZW50IHNwYWNlLiBUaGlzIG1hcHBpbmcgZmFjaWxpdGF0ZXMgYSBjbGVhciByZXByZXNlbnRhdGlvbiBvZiByZWxhdGlvbnNoaXBzIGJldHdlZW4gdXNlcnMgYW5kIGl0ZW1zLiINCkNpdGF0aW9uOiANCltTSU5HVUxBUiBWQUxVRSBERUNPTVBPU0lUSU9OIChTVkQpICYgSVRTIEFQUExJQ0FUSU9OIElOIFJFQ09NTUVOREVSIFNZU1RFTSBieSBEUi4gVkFJQkhBViBLVU1BUiwgMy8yNS8yMDIwXShodHRwczovL2FuYWx5dGljc2luZGlhbWFnLmNvbS9zaW5ndWxhci12YWx1ZS1kZWNvbXBvc2l0aW9uLXN2ZC1hcHBsaWNhdGlvbi1yZWNvbW1lbmRlci1zeXN0ZW0vIzp+OnRleHQ9SW4lMjB0aGUlMjBjb250ZXh0JTIwb2YlMjB0aGUsZ2l2ZW4lMjB0byUyMGl0ZW1zJTIwYnklMjB1c2Vycy4pDQoNCg0KIyBUb3kgRXhhbXBsZSBvZiBTVkQNCg0KSW4gdGhlIHRveSBleGFtcGxlIGJlbG93LCBJIGNyZWF0ZWQgYSAyMHgxMCBtYXRyaXggb2YgMjAgdXNlcnMgZ2l2aW5nIHJhdGluZ3MgdG8gMTAgbW92aWVzLCA1IGNvbWVkaWVzIGFuZCA1IGRyYW1hcy4gVGhpcyB3aWxsIGJlIGEgZGVuc2UgbWF0cml4IHdoZXJlIGVhY2ggdXNlciByYXRlcyBhIG1vdmllIG9uIGEgc2NhbGUgb2YgMSB0byA1LiBUaGlzIGFsc28gbWVhbnMgdGhhdCBlYWNoIHVzZXIgaGFzIHNlZW4gYWxsIDIwIG1vdmllcy5TZWNvbmRseSwgSSBhc3NpZ25lZCB0aGUgcmF0aW5ncyByYW5kb21seSB3aGljaCBhc3N1bWVzIHRoYXQgZWFjaCB1c2VyIGRvZXMgbm90IGhhdmUgYSBwYXJ0aWN1bGFyIHRhc3RlIGZvciBvbmUgZ2VucmUgb3ZlciBhbm90aGVyLiANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpzZXQuc2VlZCgxMjMpDQpBIDwtIHJvdW5kKG1hdHJpeChydW5pZigxMDAsMSw1KSxucm93PTIwLCBuY29sPTEwKSwwKQ0KbW92aWVuYW1lcyA8LSBjKCJZZXN0ZXJkYXkiLCAiS25pdmVzIE91dCIsIkpvIEpvIFJhYmJpdCIsICJHb29kIEJveXMiLCJab21iaWVsYW5kIiwgIkpva2VyIiwgIlRoZSBJcmlzaG1hbiIsICJNYXJyaWFnZSBTdG9yeSIsICJQYXJhc2l0ZSIsICJBZCBBc3RyYSIpDQpjb2xuYW1lcyhBKSA8LSBtb3ZpZW5hbWVzDQoNCg0Ka25pdHI6OmthYmxlKEEsIm1hcmtkb3duIiwgYWxpZ24gPSAnYycsIGNhcHRpb24gPSAiVG95IEV4YW1wbGUiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCkEgPC1CQm1pc2M6Om5vcm1hbGl6ZShBLCBtZXRob2QgPSAic3RhbmRhcmRpemUiKQ0KDQpgYGANCg0KDQoNCg0KVXNpbmcgdGhlIFIgZnVuY3Rpb24sIFNWRCwgSSBjcmVhdGVkIHRoZSBTVkQgbWF0cml4IGZyb20gQSBhbmQgc3RvcmVkIGVhY2ggY29tcG9uZW50IGludG8gdGhlIHZhcmlhYmxlcywgVSwgU2lnbWEsIGFuZCBWDQoNCmBgYHtyfQ0Kc3ZkX21hdHJpeCA8LSBzdmQoQSkNCmBgYA0KDQpgYGB7cn0NClU8LXN2ZF9tYXRyaXgkdSANClNpZ21hPC1kaWFnKHN2ZF9tYXRyaXgkZCkNClY8LXN2ZF9tYXRyaXgkdg0KYGBgDQoNCg0KU2lnbWEgaXMgYSBzcGVjaWFsIG1hdHJpeCB3aXRoIHplcm9lcyBleGNlcHQgb24gdGhlIGRpYWdvbmFsLg0KDQpgYGB7cn0NClNpZ21hDQpgYGANCg0KVSBpcyB0aGUgTGVmdCBTaW5ndWxhciBNYXRyaXgNCg0KYGBge3J9DQpVDQpgYGANCg0KDQpWIGlzIHRoZSBSaWdodCBTaW5ndWxhciBNYXRyaXgNCg0KYGBge3J9DQpWDQpgYGANCg0KQ29sdW1ucyBvZiBVIGFuZCBWIGFyZSBvcnRob2dvbmFsLg0KDQoNCmBgYHtyfQ0Kcm91bmQodChVKSUqJVUsMikNCmBgYA0KDQoNCmBgYHtyfQ0Kcm91bmQodChWKSUqJVYsMikNCmBgYA0KDQpUYWtpbmcgdGhlIGRvdCBwcm9kdWN0IG9mIHRoZSB0aHJlZSBtYXRyaXhlcyByZXR1cm5zIHVzIGJhY2sgdG8gdGhlIG9yaWdpbmFsIG1hdHJpeC4gDQoNCmBgYHtyfQ0Kcm91bmQoVSUqJVNpZ21hJSoldChWKSwwKQ0KYGBgDQoNCkluIHRoZSBjdXN0b20gZnVuY3Rpb24gYmVsb3csIEkgcmVkdWNlZCB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMgaW4gb3JkZXIgdG8gYWNoaWV2ZSA4MCUgb2YgdGhlICJlbmVyZ3kiIG9mIHRoZSBvcmlnaW5hbCBkaWFnb25hbCBtYXRyaXggU2lnbWEuIA0KDQoNCmBgYHtyfQ0KZ2V0X3JlZHVjZWRfZGltZW5zaW9ucyA8LSBmdW5jdGlvbihkKXsNCiAgI2NhcHR1cmUgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zDQogIG4gPC0gbGVuZ3RoKGQpDQogICNjb21wdXRlIHRoZSBlbmVyZ3kgb2YgYWxsIGRpbWVuc2lvbnMNCiAgZW5lcmd5IDwtIHN1bSgoZCleMikNCiAgZm9yIChpIGluIDE6bi0xKXsNCiAgICByZWR1Y2VkX2RpbWVuc2lvbnMgPC0gbi1pDQogICAgbmV3X2VuZXJneSA8LSByb3VuZCgoc3VtKChkWzE6cmVkdWNlZF9kaW1lbnNpb25zXSleMikgLyBlbmVyZ3kpLDIpDQogICAgDQogICAgaWYgKG5ld19lbmVyZ3kgPCAuODUpew0KICAgICAgICBicmVhaw0KICAgICAgfQ0KICAgDQogIH0NCiAgcmV0dXJuIChyZWR1Y2VkX2RpbWVuc2lvbnMpDQogIA0KICB9DQpgYGANCg0KRm9yIHRoaXMgZXhhbXBsZSwgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zIGlzIHJlZHVjZWQgdG8gMi4gDQoNCmBgYHtyfQ0KcmVkdWNlZF9kaW1lbnNpb25zIDwtZ2V0X3JlZHVjZWRfZGltZW5zaW9ucyhzdmRfbWF0cml4JGQpDQpyZWR1Y2VkX2RpbWVuc2lvbnMNCmBgYA0KDQpCZWxvdywgd2Ugc2VlIHRoYXQgd2UgY2FuIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMgZG93biB0byBqdXN0IDEgYW5kIHN0aWxsIHJldGFpbiAuOTMzIG9mIHRoZSBlbmVyZ3kgb2YgdGhlIG9yaWdpbmFsIGRpYWdvbmFsIG1hdHJpeC4NCg0KYGBge3J9DQpzdW0oKHN2ZF9tYXRyaXgkZFsxOnJlZHVjZWRfZGltZW5zaW9uc10pXjIpIC8gc3VtKChzdmRfbWF0cml4JGQpXjIpDQpgYGANCg0KSW4gdGhlIGNvZGUgYmxvY2tzIGJlbG93IHdlIHNlZSB0aGUgdGhyZWUgY29tcG9uZW50IG1hdHJpeGVzIHJlZHVjZWQuIA0KDQpgYGB7cn0NClNpZ21hIDwtIGRpYWcoc3ZkX21hdHJpeCRkWzE6cmVkdWNlZF9kaW1lbnNpb25zXSkNCg0KYGBgDQoNCg0KDQpgYGB7cn0NClVfcHJpbWUgPC0gVVssIDE6cmVkdWNlZF9kaW1lbnNpb25zXQ0KVl9wcmltZSA8LSBWWzE6cmVkdWNlZF9kaW1lbnNpb25zLF0NCmBgYA0KDQoNCmBgYHtyfQ0KVV9wcmltZSANCmBgYA0KDQoNCmBgYHtyfQ0KVl9wcmltZQ0KYGBgDQoNCiMjIFVzZXIgcmVjb21tZW5kYXRpb24NCg0KSW4gdGhlIGNvZGUgYmxvY2tzIGJlbG93LCB3ZSBtYXAgdHdvIG5ldyB1c2VycyBvbnRvIHRoZSBjb25jZXB0IHNwYWNlLiBFdmVuIHRob3VnaCBib3RoIHVzZXJzIHJhdGVkIGEgY29tZWQoaWVzKSBoaWdoZXIsIHdlIGRvIG5vdCBzZWUgYSBzaW1pbGFyaXR5IGluIHRlcm1zIG9mIHRoZSB0d28gZGltZW5zaW9uYWwgY29uY2VwdCBzcGFjZSBiZXR3ZWVuIHRoZSB0d28gdXNlcnMuICANCg0KDQpUaGlzIG1heSBiZSBkdWUgdG8gaG93IHRoZSBvcmlnaW5hbCBtYXRyaXggaW4gdGhpcyBleGFtcGxlIHdhcyBjcmVhdGVkIGJ5IGFzc2lnbmluZyB0aGUgcmF0aW5ncyByYW5kb21seSwgbm90IGFjY291bnRpbmcgZm9yIHBlcnNvbmFsIHRhc3RlcyBvZiB0aGUgdXNlcnMuIEFsc28sIHRoZXJlJ3Mgc3BhcnNpdHkgaW4gdGhlc2UgdHdvIG1hdHJpeGVzLiANCg0KDQpgYGB7cn0NCg0KbmV3X3VzZXIgPC0gbWF0cml4KGMoNSwwLDAsMCwwLDAsMSwwLDEsMSksbnJvdz0xLCBuY29sPTEwKQ0KbmV3X3VzZXINCg0KYGBgDQoNCmBgYHtyfQ0KbmV3X3VzZXJfbWF0cml4IDwtIG5ld191c2VyJSoldChWX3ByaW1lKQ0KbmV3X3VzZXJfbWF0cml4DQpgYGANCg0KDQpgYGB7cn0NCm5ld191c2VyMiA8LSBtYXRyaXgoYygwLDQsNSw0LDAsMCwwLDAsMCwxKSxucm93PTEsIG5jb2w9MTApDQpuZXdfdXNlcjINCmBgYA0KDQpgYGB7cn0NCm5ld191c2VyMl9tYXRyaXggPC0gbmV3X3VzZXIyJSoldChWX3ByaW1lKQ0KbmV3X3VzZXIyX21hdHJpeA0KYGBgDQoNCkluIHRoZSBuZXh0IHNlY3Rpb24sIEkgYXBwbGllZCB0aGUgU1ZEIHRvIGFjdHVhbCBtb3ZpZSByYXRpbmdzIGRhdGEgdXNpbmcgdGhlIE1vdmllTGVucyBkYXRhc2V0Lg0KDQoNCiMgSW1wbGVtZW50IE1hbnVhbCBTVkQNCg0KDQojIyBJbXBvcnQgRGF0YQ0KDQpJIGJlZ2FuIGJ5IGRvd25pbmcgdGhlIHRoZSBNb3ZpZUxlbnNlIHJhdGluZyBtYXRyaXggd2hpY2ggaXMgd2l0aGluIHRoZSBSZWNvbW1lbmRlcmxhYiBSIHBhY2thZ2UgW0xpbmtdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yZWNvbW1lbmRlcmxhYi92aWduZXR0ZXMvcmVjb21tZW5kZXJsYWIucGRmKQ0KDQpgYGB7cn0NCmRhdGEoIk1vdmllTGVuc2UiKQ0KTW92aWVMZW5zZQ0KYGBgDQoNClRoZSBtb3ZpZV9yYXRpbmdzX21hdHJpeCBpcyBjb21wcmlzZWQgb2YgMTQ3IHVzZXJzIGFuZCBtb3ZpZXMgdGhhdCBoYXZlIGJlZW4gd2F0Y2hlZCBsZWFzdCAxMTYgbW92aWVzLg0KDQoNCmBgYHtyfQ0KcmF0aW5nc19tb3ZpZXMgPC0gTW92aWVMZW5zZVtyb3dDb3VudHMoTW92aWVMZW5zZSkgPiAyMDAsIGNvbENvdW50cyhNb3ZpZUxlbnNlKSA+IDIwMF0NCnJhdGluZ3NfbW92aWVzX25vcm0gPC0gcmVjb21tZW5kZXJsYWI6Om5vcm1hbGl6ZShyYXRpbmdzX21vdmllcykNCm1vdmllX3JhdGluZ3NfbWF0cml4IDwtIGFzLm1hdHJpeChyYXRpbmdzX21vdmllc19ub3JtQGRhdGEpDQpkaW0obW92aWVfcmF0aW5nc19tYXRyaXgpDQpgYGANCkkgcHV0IHRoZSBtb3ZpZV9yYXRpbmdzX21hdHJpeCB0aHJvdWdoIHRoZSBzYW1lIHByb2Nlc3MgYXMgdGhlIFRveSBtYXRyaXggZnJvbSBwYXJ0IG9uZS4gVGhlIGtleSBkaWZmZXJlbmNlcyBhcmUgdGhhdCB0aGUgbW92aWVfcmF0aW5nc19tYXRyaXggd29uJ3QgYmUgYSBkZW5zZWx5IHBvcHVsYXRlZCBhcyB0aGUgVG95IG1hdHJpeC4gQWxzbywgdGhlIHJhdGluZ3MgYXJlIG5vdCBhc3NpZ25lZCByYW5kb21seS4gIEhlcmUgd2Ugd2lsbCBoYXZlIHJlYWwgdXNlciBzZW50aW1lbnQuDQoNCmBgYHtyfQ0KQTEgPC0gc3ZkKG1vdmllX3JhdGluZ3NfbWF0cml4KQ0KYGBgDQoNCmBgYHtyfQ0KbGVuZ3RoKEExJGQpDQpgYGANCg0KDQpgYGB7cn0NCmRpbShBMSR1KQ0KYGBgDQoNCmBgYHtyfQ0KZGltKEExJHYpDQpgYGANCkkgd2FzIGFibGUgdG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgZGltZW5zaW9ucyBvZiB0aGUgbW92aWVfcmF0aW5nc19tYXRyaXggZnJvbSAxMTYgdG8gNDYuIA0KDQpgYGB7cn0NCnJlZHVjZWRfZGltZW5zaW9uczIgPC1nZXRfcmVkdWNlZF9kaW1lbnNpb25zKEExJGQpDQpyZWR1Y2VkX2RpbWVuc2lvbnMyDQpgYGANCg0KYGBge3J9DQpzdW0oKEExJGRbMTpyZWR1Y2VkX2RpbWVuc2lvbnMyXSleMikgLyBzdW0oKEExJGQpXjIpDQpgYGANCkkgcmVkdWNlZCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgY29tcG9uZW50cyBvZiB0aGUgbWFpbiBtYXRyaXguIA0KDQpgYGB7cn0NCg0KQTFEIDwtQTEkZFsxOnJlZHVjZWRfZGltZW5zaW9uczJdIA0KU2lnbWEyIDwtIGRpYWcoQTEkZFsxOnJlZHVjZWRfZGltZW5zaW9uczJdKQ0KQTFVX3ByaW1lIDwtIEExJHVbLCAxOnJlZHVjZWRfZGltZW5zaW9uczJdDQpBMVZfcHJpbWUgPC0gQTEkdlsxOnJlZHVjZWRfZGltZW5zaW9uczIsXQ0KYGBgDQoNCiMjIFVzZXIgMzUNCg0KSGVyZSwgSSBwdXQgdGhlIFNWRCByZWNvbW1lbmRlciB0byB0aGUgdGFzayBvZiByZWNvbW1lbmRpbmcgbW92aWVzIHRvIHVzZXIgMzUuIEZpcnN0LCBJIHByb2plY3RlZCB1c2VyIDM1J3MgcmF0aW5ncyBvbnRvIHRoZSBtb3ZpZXMgY29uY2VwdCBzcGFjZSBieSB0YWtpbmcgdGhlIGRvdCBwcm9kdWN0IG9mIHVzZXIgMzUncyByYXRpbmdzIHdpdGggdGhlIGNvbmNlcHQgc3BhY2UgZGVub3RlZCBieSB0aGUgdHJhbnNwb3NlIG9mIEExVi4gDQoNCkkgZm91bmQgMTEgbW92aWVzIHRoYXQgdGhlIFNWRCByZWNvbW1lbmRlciB3b3VsZCByZWNvbW1lbmQgKD4xKSB0byB1c2VyIDM1DQoNCmBgYHtyfQ0KI1Byb2plY3QgdGhlIHJhdGluZ3Mgb2YgdXNlciAzNSBvbnRvIHRoZSBjb25jZXB0IHNwYWNlLiANCnN2ZF9yZWMgPC0gbW92aWVfcmF0aW5nc19tYXRyaXhbMzUsXSAlKiUgdChBMVZfcHJpbWUpDQpzdmRfcmVjX2RmIDwtIGFzLmRhdGEuZnJhbWUodChzdmRfcmVjKSkNCnN2ZF9yZWNfZGZbIlRpdGxlcyJdIDwtIGNvbG5hbWVzKG1vdmllX3JhdGluZ3NfbWF0cml4KVsxOnJlZHVjZWRfZGltZW5zaW9uczJdDQpjb2xuYW1lcyhzdmRfcmVjX2RmKSA8LSBjKCJTVkRfUmF0aW5ncyIsICJUaXRsZXMiKQ0Kc3ZkX3JlY19kZiA8LSBzdmRfcmVjX2RmWyxjKDIsMSldDQpyb3duYW1lcyhzdmRfcmVjX2RmKSA8LSAgYygxOm5yb3coc3ZkX3JlY19kZikpDQpzdmRfcmVjX2RmIDwtIHN2ZF9yZWNfZGYgJT4lIA0KICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoU1ZEX1JhdGluZ3MpKQ0KDQpzdmRfcmVjX2RmW3N2ZF9yZWNfZGYkU1ZEX1JhdGluZ3MgPjEsXQ0KYGBgDQpgYGB7cn0NCnJlY29tbWVuZGVkX3RpdGxlcyA8LSBhcy5tYXRyaXgoc3ZkX3JlY19kZltzdmRfcmVjX2RmJFVzZXIzNV9SYXRpbmdzID4xLF0pDQpgYGANCg0KDQpPZiB0aGVzZSAxMSBtb3ZpZXMgdGhhdCBTVkQgcmVjb21tZW5kZWQsIFVzZXIgMzUgaGFkIHJhdGVkIHRocmVlIG9mIHRoZW0uIE5vdCByYXRlZCA0LCBhbmQgcmF0ZWQgYW5vdGhlciA0IG5lZ2F0aXZlbHkuDQoNCg0KYGBge3J9DQp1c2VyU2FtcGxlMzUgPC0gYXMuZGF0YS5mcmFtZShtb3ZpZV9yYXRpbmdzX21hdHJpeFszNSxdKQ0KdXNlclNhbXBsZTM1WyJUaXRsZXMiXSA8LSByb3duYW1lcyh1c2VyU2FtcGxlMzUpDQp1c2VyU2FtcGxlMzUgPC0gdXNlclNhbXBsZTM1WyxjKDIsMSldDQpjb2xuYW1lcyh1c2VyU2FtcGxlMzUpPC0gYygiVGl0bGVzIiwgIlVzZXIzNV9SYXRpbmciKQ0Kcm93bmFtZXModXNlclNhbXBsZTM1KSA8LSBjKDE6bnJvdyh1c2VyU2FtcGxlMzUpKQ0KdXNlclNhbXBsZTM1IDwtIHVzZXJTYW1wbGUzNSAlPiUgDQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhVc2VyMzVfUmF0aW5nKSkNCg0KcmVjb21tZW5kZWRfdG9fMzUgPC0gdXNlclNhbXBsZTM1W3VzZXJTYW1wbGUzNSRUaXRsZXMgJWluJSByZWNvbW1lbmRlZF90aXRsZXMsXQ0KDQpyZWNvbW1lbmRlZF90b18zNQ0KYGBgDQpCZWxvdyBhcmUgdGhlIDQgbW92aWVzIHRoYXQgdGhlIFNWRCByZWNvbW1lbmRlZCB0byBVc2VyIDM1IHRoYXQgVXNlciAzNSBoYWQgcmF0ZWQgdmVyeSBsb3dseS4gDQoNCmBgYHtyfQ0KdXNlcjM1X2Rpc2xpa2VzIDwtIHVzZXJTYW1wbGUzNVt1c2VyU2FtcGxlMzUkVGl0bGVzICVpbiUgcmVjb21tZW5kZWRfdGl0bGVzICYgdXNlclNhbXBsZTM1JFVzZXIzNV9SYXRpbmcgPCAwLF0NCg0KdXNlcjM1X2Rpc2xpa2VzDQpgYGANCg0KSW4gc3VtbWFyeSwgd2UgY2FuIHNheSB0aGF0IGEgbWFudWFsIGltcGxlbWVudGF0aW9uIG9mIHRoZSBTVkQgeWllbGRlZCBhYm91dCBhIDI3JSByZWNvbW1lbmRhdGlvbiBzdWNjZXNzIHJhdGUgd2l0aCBhIDM2JSBmYWlsdXJlIHJhdGUuIFdlIGNhbiBhbHNvIHNheSB0aGF0IG9mIHRoZSA0IG1vdmllcyB0aGF0IHRoZSBTVkQgcmVjb21tZW5kZWQgdG8gVXNlciAzNSwgcy9oZSB3aWxsIGxpa2Ugb25seSBvbmUgb2YgdGhlbS4gTW9yZSB0aGFuIGxpa2VseSwgIlVzdWFsIFN1c3BlY3RzLCBUaGUgKDE5OTUpIi4NCg0KSW4gdGhlIGZpbmFsIHNlY3Rpb24sIEkgdGFrZSBhIG1vcmUgZm9ybWFsIGV2YWx1YXRpb24gYXBwcm9hY2ggYW5kIGNvbXBhcmUgdGhlIFNWRCBtb2RlbCB0byB0aGUgVXNlciBCYXNlZCBDb2xsYWJvcmF0aXZlIEZpbHRlcmluIG1vZGVsLiANCg0KIyBTVkQgdnMuIFVDQkYNCg0KIyMgSy1Gb2xkIFNwbGl0DQoNCg0KYGBge3J9DQpuX2ZvbGQgPC0gOA0KaXRlbXNfdG9fa2VlcCA8LSAxMA0KcmF0aW5nX3RocmVzaG9sZCA8LSAzDQpgYGANCg0KDQpgYGB7cn0NCmtfZm9sZF9zcGxpdCA8LSBmdW5jdGlvbihkYXRhLCBtZXRob2QsIG5fZm9sZCwgaXRlbXNfdG9fa2VlcCwgcmF0aW5nX3RocmVzaG9sZCl7DQogIA0KICAgICAgICByZXR1cm4oZXZhbHVhdGlvblNjaGVtZShkYXRhID0gZGF0YSwgbWV0aG9kID0gbWV0aG9kLCBrID0gbl9mb2xkLCBnaXZlbiA9IGl0ZW1zX3RvX2tlZXAsIGdvb2RSYXRpbmcgPSByYXRpbmdfdGhyZXNob2xkKSkNCiAgDQogIH0NCmBgYA0KDQpgYGB7cn0NCmV2YWxfc2V0cyA8LSBrX2ZvbGRfc3BsaXQocmF0aW5nc19tb3ZpZXMsICJjcm9zcy12YWxpZGF0aW9uIiwgbl9mb2xkLCBpdGVtc190b19rZWVwLCByYXRpbmdfdGhyZXNob2xkKSANCiAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQoNCmBgYHtyfQ0Kc2l6ZV9zZXRzIDwtIHNhcHBseShldmFsX3NldHNAcnVuc1RyYWluLCBsZW5ndGgpDQpzaXplX3NldHMNCmBgYA0KDQoNCiMjIEV2YWx1YXRpb24gb2YgbW9kZWxzDQoNClRvIGV2YWx1YXRlIHRoZSB0d28gbW9kZWxzLCBJIGNyZWF0ZWQgYSBsaXN0IG9mIFVCQ0YgYW5kIFNWRCBtb2RlbHMgd2l0aCBkaWZmZXJlbnQgcGFyYW1ldGVycyAocGVhcnNvbiwgY29zaW5lLCBhbmQgbnVsbCBmb3IgdGhlIFNWRCBtb2RlbCkuIFRoaXMgbGlzdCB3YXMgZmVkIGludG8gdGhlIGV2YWx1YXRlIGZ1bmN0aW9uIGFuZCBiZWxvdy4gVGhlIHZpc3VhbGl6YXRpb25zIGJlbG93IGJvdGggc2hvdyB0aGF0IHRoZSBVQkNGIHVzaW5nIHRoZSAicGVhcnNvbiIgbWV0aG9kIGhhZCB0aGUgbGFyZ2VyIGFyZWEgdW5kZXIgdGhlIGN1cnZlLiANCg0KDQoNCmBgYHtyfQ0KcmVjb21tZW5kZXJfbW9kZWxzIDwtIHJlY29tbWVuZGVyUmVnaXN0cnkkZ2V0X2VudHJpZXMoZGF0YVR5cGUgPSJyZWFsUmF0aW5nTWF0cml4IikNCnJlY29tbWVuZGVyX21vZGVscyRTVkRfcmVhbFJhdGluZ01hdHJpeCRwYXJhbWV0ZXJzDQpgYGANCg0KDQpgYGB7cn0NCnJlY29tbWVuZGVyX21vZGVscyRVQkNGX3JlYWxSYXRpbmdNYXRyaXgkcGFyYW1ldGVycw0KYGBgDQoNCg0KDQoNCg0KYGBge3J9DQptb2RlbHNfdG9fZXZhbHVhdGUgPC0gbGlzdChVQkNGX2NvcyA9IGxpc3QobmFtZSA9ICJVQkNGIiwgcGFyYW0gPSBsaXN0KG1ldGhvZCA9ImNvc2luZSIpKSwNClVCQ0ZfY29yID0gbGlzdChuYW1lID0gIlVCQ0YiLCBwYXJhbSA9IGxpc3QobWV0aG9kID0icGVhcnNvbiIpKSwNClNWRCA9IGxpc3QobmFtZSA9ICJTVkQiLCBwYXJhbT1OVUxMKSkNCg0Kbl9yZWNvbW1lbmRhdGlvbnMgPC0gYygxLCA1LCBzZXEoMTAsIDEwMCwgMTApKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNvbXBhcmVfcmVzdWx0cyA8LSBldmFsdWF0ZSh4ID0gZXZhbF9zZXRzLCBtZXRob2QgPSBtb2RlbHNfdG9fZXZhbHVhdGUsIG49IG5fcmVjb21tZW5kYXRpb25zKQ0KYGBgDQoNCg0KYGBge3J9DQpwbG90KGNvbXBhcmVfcmVzdWx0cywgYW5ub3RhdGUgPSAxLCBsZWdlbmQgPSAidG9wbGVmdCIpICsgdGl0bGUoIlJPQyBjdXJ2ZSIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KcGxvdChjb21wYXJlX3Jlc3VsdHMsICJwcmVjL3JlYyIsIGFubm90YXRlID0gMSwgbGVnZW5kID0gImJvdHRvbXJpZ2h0IikNCnRpdGxlKCJQcmVjaXNpb24tcmVjYWxsIikNCmBgYA0KDQoNCg0KYGBge3J9DQphdmdfbWF0cmljZXMgPC0gbGFwcGx5KGNvbXBhcmVfcmVzdWx0cywgYXZnKQ0KYXZnX21hdHJpY2VzDQoNCmBgYA0KDQojIFJNU0UgLSBTVkQgdnMgVUJDRg0KDQojIyBTVkQNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQptb2RlbF90b19ldmFsdWF0ZSA8LSAiU1ZEIg0KbW9kZWxfcGFyYW1ldGVycyA8LSBOVUxMDQpTVkRfZXZhbF9yZWNvbW1lbmRlciA8LSBSZWNvbW1lbmRlcihkYXRhID0gZ2V0RGF0YShldmFsX3NldHMsICJ0cmFpbiIpLG1ldGhvZCA9IG1vZGVsX3RvX2V2YWx1YXRlLCBwYXJhbWV0ZXIgPSBtb2RlbF9wYXJhbWV0ZXJzKQ0KaXRlbXNfdG9fcmVjb21tZW5kIDwtIDUNClNWRF9ldmFsX3ByZWRpY3Rpb24gPC0gcHJlZGljdChvYmplY3QgPSBTVkRfZXZhbF9yZWNvbW1lbmRlciwgbmV3ZGF0YT1nZXREYXRhKGV2YWxfc2V0cywgImtub3duIiksIG4gPSBpdGVtc190b19yZWNvbW1lbmQsIHR5cGUgPSAicmF0aW5ncyIpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpTVkRfZXZhbF9hY2N1cmFjeSA8LSBjYWxjUHJlZGljdGlvbkFjY3VyYWN5KHggPSBTVkRfZXZhbF9wcmVkaWN0aW9uLCBkYXRhID0gZ2V0RGF0YShldmFsX3NldHMsICJ1bmtub3duIiksIGJ5VXNlciA9VFJVRSkNCmhlYWQoU1ZEX2V2YWxfYWNjdXJhY3kpDQpgYGANCg0KDQoNCmBgYHtyfQ0KU1ZEX2V2YWxfYWNjdXJhY3lfZnVsbCA8LSBjYWxjUHJlZGljdGlvbkFjY3VyYWN5KHggPSBTVkRfZXZhbF9wcmVkaWN0aW9uLCBkYXRhID0gZ2V0RGF0YShldmFsX3NldHMsICJ1bmtub3duIiksIGJ5VXNlciA9RkFMU0UpDQpTVkRfZXZhbF9hY2N1cmFjeV9mdWxsDQpgYGANCg0KDQpgYGB7cn0NCnFwbG90KFNWRF9ldmFsX2FjY3VyYWN5WywgIlJNU0UiXSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMTc1KSArIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiB0aGUgUk1TRSBieSB1c2VyIikNCmBgYA0KDQoNCiMjIFVCQ0YNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQptb2RlbF90b19ldmFsdWF0ZSA8LSAiVUJDRiINCm1vZGVsX3BhcmFtZXRlcnMgPC0gInBlYXJzb24iDQpVQkNGX2V2YWxfcmVjb21tZW5kZXIgPC0gUmVjb21tZW5kZXIoZGF0YSA9IGdldERhdGEoZXZhbF9zZXRzLCAidHJhaW4iKSxtZXRob2QgPSBtb2RlbF90b19ldmFsdWF0ZSwgcGFyYW1ldGVyID0gbW9kZWxfcGFyYW1ldGVycykNCml0ZW1zX3RvX3JlY29tbWVuZCA8LSA1DQpVQkNGX2V2YWxfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG9iamVjdCA9IFVCQ0ZfZXZhbF9yZWNvbW1lbmRlciwgbmV3ZGF0YT1nZXREYXRhKGV2YWxfc2V0cywgImtub3duIiksIG4gPSBpdGVtc190b19yZWNvbW1lbmQsIHR5cGUgPSAicmF0aW5ncyIpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpVQkNGX2V2YWxfYWNjdXJhY3kgPC0gY2FsY1ByZWRpY3Rpb25BY2N1cmFjeSh4ID0gVUJDRl9ldmFsX3ByZWRpY3Rpb24sIGRhdGEgPSBnZXREYXRhKGV2YWxfc2V0cywgInVua25vd24iKSwgYnlVc2VyID1UUlVFKQ0KaGVhZChVQkNGX2V2YWxfYWNjdXJhY3ksMjApDQpgYGANCg0KDQoNCmBgYHtyfQ0KVUJDRl9ldmFsX2FjY3VyYWN5X2Z1bGwgPC0gY2FsY1ByZWRpY3Rpb25BY2N1cmFjeSh4ID0gVUJDRl9ldmFsX3ByZWRpY3Rpb24sIGRhdGEgPSBnZXREYXRhKGV2YWxfc2V0cywgInVua25vd24iKSwgYnlVc2VyID1GQUxTRSkNClVCQ0ZfZXZhbF9hY2N1cmFjeV9mdWxsDQpgYGANCg0KDQoNCmBgYHtyfQ0KcXBsb3QoVUJDRl9ldmFsX2FjY3VyYWN5WywgIlJNU0UiXSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMTc1KSArIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiB0aGUgUk1TRSBieSB1c2VyIikNCmBgYA0KDQojIENvbmNsdXNpb24NCg0KVGhpcyBwcm9qZWN0IGxvb2tlZCBhdCB0aGUgU1ZEIG1vZGVsIGFnYWluc3QgcmFuZG9tbHkgYXNzaWduZWQgdXNlciByYXRpbmdzLCB1c2VyIHJhdGluZ3MgZnJvbSB0aGUgTW92aWVMZW5zIGRhdGFzZXQsIGFuZCBkaWQgYSBmb3JtYWwgY29tcGFyaXNvbiBiZXR3ZWVuIFNWRCBhbmQgdGhlIFVzZXIgQmFzZWQgQ29sbGFib3JhdGl2ZSBmaWx0ZXJzLiBXZSBzYXcgdGhhdCB0aGUgU1ZEIG1vZGVsIHBlcmZvcm1lZCBwb29ybHkgYWdhaW5zdCByYW5kb21seSBhc3NpZ25lZCByYXRpbmdzLCBpbXByb3ZlZCBzb21ld2hhdCBhZ2FpbnN0IGFjdHVhbCB1c2VyIHJhdGluZ3MsIGFuZCBkaWQgbm90IHBlcmZvcm0gYXMgd2VsbCBhcyB0aGUgVUJDRiBtb2RlbHMuIA0KDQoNCg0KIyBSZWZlcmVuY2VzDQoNCg0KDQpbU2luZ3VsYXIgVmFsdWUgRGVjb21wb3NpdGlvbiAoU1ZEKSB0dXRvcmlhbCAtIE1JVF0oaHR0cDovL3dlYi5taXQuZWR1L2NvdXJzZS9vdGhlci9iZS40MDAvT2xkRmlsZXMvd3d3L1NWRC9TaW5ndWxhcl9WYWx1ZV9EZWNvbXBvc2l0aW9uLmh0bSkNCg0KW1NJTkdVTEFSIFZBTFVFIERFQ09NUE9TSVRJT04gKFNWRCkgJiBJVFMgQVBQTElDQVRJT04gSU4gUkVDT01NRU5ERVIgU1lTVEVNIGJ5IERSLiBWQUlCSEFWIEtVTUFSLCAzLzI1LzIwMjBdKGh0dHBzOi8vYW5hbHl0aWNzaW5kaWFtYWcuY29tL3Npbmd1bGFyLXZhbHVlLWRlY29tcG9zaXRpb24tc3ZkLWFwcGxpY2F0aW9uLXJlY29tbWVuZGVyLXN5c3RlbS8jOn46dGV4dD1JbiUyMHRoZSUyMGNvbnRleHQlMjBvZiUyMHRoZSxnaXZlbiUyMHRvJTIwaXRlbXMlMjBieSUyMHVzZXJzLikNCg0KDQpbTGVjdHVyZXMgNDcgdGhyb3VnaCA1MCBTdGFuZm9yZCBVbml2ZXJzaXR5IFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb25dKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2NoYW5uZWwvVUM1eng4T3dpam12LWJiaEFLNlo5YXBnL3NlYXJjaD9xdWVyeT1TVkQpDQoNCg==