Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing regular ALS #19

Closed
leodesigner opened this issue Feb 17, 2017 · 8 comments
Closed

Implementing regular ALS #19

leodesigner opened this issue Feb 17, 2017 · 8 comments

Comments

@leodesigner
Copy link

leodesigner commented Feb 17, 2017

Hi Ben,
I am trying to modify your code to work with regular ALS matrix factorization algorithm (for sparce matrices)
This code seems working for now.
However could you please take a look and verify correctness of the proposed changes ?

def least_squares(Cui, X, Y, regularization, num_threads=0):
    users, factors = X.shape
    E = np.eye(factors)
    for u in range(users):
        A = np.zeros(shape=(factors,factors))
        b = np.zeros(factors)

        # confidence = 1, Pu = confidence
        
        for i, confidence in nonzeros(Cui, u):
            factor = Y[i]
            A += np.outer(factor, factor)
            b += 1 * factor * confidence

        A += regularization * E
        X[u] = np.linalg.solve(A, b)

@benfred
Copy link
Owner

benfred commented Feb 26, 2017

Whats the loss function you're trying to optimize here? If you're going for X[u] = (Pui - XuYi)^2 I think thats correct - but there are substantially faster ways of doing this

Since A = YtY + regI is the same for all users, you break the solving stage up and calculate the cholesky decomposition out of the loop, and then just use that to solve for different 'b' values.

The code for this would look something like

YtY = Y.dot(Y.T) + reg * numpy.eye(Y.shape[0])
U, err = scipy.linalg.lapack.dpotrf(YtY)

for u in range(users):
    b = np.zeros(factors)        
    for i, preference in nonzeros(Pui, u):
        b += Y[i] * preference
    X[u] = scipy.linalg.lapack.dpotrs(U, b)[0]

Note that I haven't tested this out - but I think the basic idea should work

@leodesigner
Copy link
Author

Thank you, yes the loss function is X[u] = (Pui - XuYi)^2
I will test your code. Still playing with different loss functions on my dataset.
Seems your implicit ALS code is more stable comparing to Conjugate Gradient Method.
I am getting 'nan' values as the results in X and Y with cg=True.
However, if I will lower number of iterations to 5 (from 15) it seems to produce the same results as implicit ALS.

@benfred
Copy link
Owner

benfred commented Feb 28, 2017

Would it be possible for you to email me the matrix causing the issue with the CG code? I'm interested in seeing if I can fix

@leodesigner
Copy link
Author

Cu matrix is not too big:
Here is the my debug output:

...
C.shape (12, 12), Before Factorization (C.toarray()):
[[ 0.          2.          1.5         1.33333333  1.25        1.2         0.
   0.          0.          0.          0.          0.        ]
 [ 0.          0.          2.          1.5         1.33333333  1.25        0.
   0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          2.          1.5         1.33333333
   0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          2.          1.5         0.
   0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          0.          2.          0.
   0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.
   2.          1.5         1.33333333  1.25        1.2       ]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          2.          1.5         1.33333333  1.25      ]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          0.          2.          1.5         1.33333333]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          0.          0.          2.          1.5       ]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          0.          0.          0.          2.        ]
 [ 0.          0.          0.          0.          0.          0.          0.
   0.          0.          0.          0.          0.        ]]
Running factorization...
X
[[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]
....

Here is the configuration for matrix factorization:

dtype = np.float64 
mf_n_factors = 3
regularization = 0.01
iterations = 15 # 5 for cg, 15 for ALS
use_native = True
#cg = False
cg = True

users, items = C.shape

X = np.random.rand(users, mf_n_factors).astype(dtype) * 0.01
Y = np.random.rand(items, mf_n_factors).astype(dtype) * 0.01

X,Y = alternating_least_squares(C,
							X,
							Y,
                                 factors=mf_n_factors,
                                 regularization=regularization,
                                 iterations=iterations,
                                 use_native=use_native,
                                 dtype=dtype,
                                 use_cg=cg,
                                 calculate_training_loss=True)

After changing iterations to 5, or changing mf_n_factors to 5
I am getting the same results as with implicit ALS.

@benfred
Copy link
Owner

benfred commented Feb 28, 2017

Thanks! will dig in on the weekend and hopefully fix

benfred added a commit that referenced this issue Mar 2, 2017
#19 (comment)

There was an issue with certain small matrices used as inputs causing NaN
values to appear in the CG optimizer. The reason seems to be a divide by zero
when rsold approached zero. Added a check to early exit in that case since the
optimization has succeeded at that point.
@benfred
Copy link
Owner

benfred commented Mar 2, 2017

Turned out to be a simple fix , that last commit should resolve this issue. Thanks for letting me know!

@leodesigner
Copy link
Author

Thanks ! I was suspecting division by zero too.

@yangji9181
Copy link

Hi Ben,

It feels like the current code is still suffering this issue same: when I tried to run the 'basic usage' on a few small 100*200 random 0-1 matrixes, the output scores are always nan. Could you try to look into that again?

Thanks.

@yangji9181 yangji9181 mentioned this issue Feb 7, 2018
benfred added a commit that referenced this issue May 23, 2018
With synthetic data and a large regularization parameter, the CG ALS model
would converge so that some users/items factors had 0 vectors for solutions.
The CG update would fail in this case setting all the factors to NaN.
(#106).

Fix by detecting when this would occur and aborting. A previous
check handled the case inside the loop:
#19 (comment)
benfred added a commit that referenced this issue May 23, 2018
With synthetic data and a large regularization parameter, the CG ALS model
would converge so that some users/items factors had 0 vectors for solutions.
The CG update would fail in this case setting all the factors to NaN.
(#106).

Fix by detecting when this would occur and aborting. A previous
check handled the case inside the loop:
#19 (comment)
benfred added a commit that referenced this issue May 23, 2018
With synthetic data and a large regularization parameter, the CG ALS model
would converge so that some users/items factors had 0 vectors for solutions.
The CG update would fail in this case setting all the factors to NaN.
(#106).

Fix by detecting when this would occur and aborting. A previous
check handled the case inside the loop:
#19 (comment), this handles
the case where rsold = 0 entering the loop.
benfred added a commit that referenced this issue May 23, 2018
With synthetic data and a large regularization parameter, the CG ALS model
would converge so that some users/items factors had 0 vectors for solutions.
The CG update would fail in this case setting all the factors to NaN.
(#106).

Fix by detecting when this would occur and aborting. A previous
check handled the case inside the loop:
#19 (comment), this handles
the case where rsold = 0 entering the loop.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants