├── BPMF.py ├── BPTF.py ├── LDA.py ├── PMF.py ├── README.md ├── config.ini └── wishart.py /BPMF.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/env python 2 | #coding:utf-8 3 | 4 | from readConfig import ReadConfig 5 | import numpy as np 6 | 7 | 8 | class BPMF: 9 | def __init__(self, trainMatrix, testMatrix, cf): 10 | self.trainMatrix = trainMatrix 11 | self.testMatrix = testMatrix 12 | self.cf = cf 13 | self.numUsers = 0 14 | self.numItems = 0 15 | 16 | def getParameters(self): 17 | self.numFactors = int(self.cf.getParameter('BPMF', 'numFactors')) 18 | self.MAX_Iteration = int(self.cf.getParameter('BPMF', 'MAX_Iteration')) 19 | 20 | def initGaussian(self, matrix, mean, var): 21 | '''''' 22 | return np.random.normal(mean, var, size=matrix.shape) 23 | 24 | def generateUVHyperParameters(self, matrix, N): 25 | # The prior of matrix 26 | beta0 = 2.0 27 | mu0 = np.zeros(self.numFactors) 28 | X_bar = matrix.mean(axis=1) 29 | v0 = self.numFactors 30 | # mu0_post = beta0 * mu0 + N* 31 | 32 | 33 | def buildModel(self): 34 | ''' 35 | ''' 36 | beta = 2 37 | mu_u = np.zeros(self.numFactors) 38 | mu_m = np.zeros(self.numFactors) 39 | 40 | # parameters of Inv-Whishart distribution 41 | WI_u = np.eye(self.numFactors) 42 | b0_u = 2 43 | df_u = self.numFactors 44 | mu0_u = np.zeros(self.numFactors) 45 | 46 | WI_m = np.eye(self.numFactors) 47 | b0_m = 2 48 | df_m = self.numFactors 49 | mu0_m = np.zeros(self.numFactors) 50 | 51 | # initializing Bayesian PMF using MAP solution found by PMF 52 | P = np.zeros((self.numUsers, self.numFactors)) 53 | Q = np.zeros((self.numItems, self.numFactors)) 54 | 55 | P = self.initGaussian(P, 0, 1) 56 | Q = self.initGaussian(Q, 0, 1) 57 | 58 | mu_u = P.mean(axis=1) 59 | mu_m = Q.mean(axis=1) 60 | 61 | alpha_u = np.asarray(np.asmatrix(np.var(P, axis=1))**(-1)) 62 | alpha_m = np.asarray(np.asmatrix(np.var(Q, axis=1))**(-1)) 63 | 64 | # Iteration: 65 | x_bar = np.zero(self.numFactors) 66 | normalRdn = np.zero(self.numFactors) 67 | 68 | M = self.numUsers 69 | N = self.numItems 70 | 71 | for iter in range(1, self.MAX_Iteration): 72 | 73 | # Sample from user hyper parameters 74 | x_bar = P.mean(axis=1) 75 | S_bar = np.var(P, axis=1) 76 | 77 | mu0_u_x_bar = mu0_u - x_bar 78 | e1e2 = np.asmatrix(mu0_u_x_bar).transpose() * np.asmatrix(mu0_u_x_bar) 79 | e1e2 /= (M * b0_u / (b0_u + M + 0.0)) 80 | 81 | WI_post = np. 82 | WI_post = WI_u.inv().add(S_bar.scale(M)).add(e1e2); 83 | WI_post = WI_post.inv(); 84 | WI_post = WI_post.add(WI_post.transpose()).scale(0.5); 85 | 86 | df_upost = df_u + M; 87 | DenseMatrix wishrnd_u = wishart(WI_post, df_upost); 88 | if (wishrnd_u != null) 89 | alpha_u = wishrnd_u; 90 | mu_temp = mu0_u.scale(b0_u).add(x_bar.scale(M)).scale(1 / (b0_u + M + 0.0)); 91 | lam = alpha_u.scale(b0_u + M).inv().cholesky(); 92 | 93 | if (lam != null) { 94 | lam = lam.transpose(); 95 | 96 | for (int f = 0; f < numFactors; f++) 97 | normalRdn.set(f, Randoms.gaussian(0, 1)); 98 | 99 | mu_u = lam.mult(normalRdn).add(mu_temp); 100 | } -------------------------------------------------------------------------------- /BPTF.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/env python 2 | #coding:utf-8 3 | 4 | from readConfig import ReadConfig 5 | import numpy as np 6 | from wishart import wishrnd 7 | 8 | class BPTF: 9 | def __init__(self, trainTensor, testTensor, cf): 10 | self.trainTensor = trainTensor 11 | self.testTensor = testTensor 12 | self.cf = cf 13 | 14 | def getHyperParameters(self): 15 | self.numFactors = int(self.cf.getParameter('BPTF', 'numFactors')) 16 | self.nuAlpha = int(self.cf.getParameter('BPTF', 'nuAlpha')) 17 | self.Walpha = int(self.cf.getParameter('BPTF', 'Walpha')) 18 | self.mu0 = int(self.cf.getParameter('BPTF', 'mu')) 19 | self.muT0 = int(self.cf.getParameter('BPTF', 'muT')) 20 | self.beta0 = int(self.cf.getParameter('BPTF', 'beta')) 21 | self.W0 = np.eye(self.numFactors) 22 | self.W0T = np.eye(self.numFactors) 23 | 24 | self.iWalpha = 1.0/self.Walpha 25 | 26 | 27 | def generateUVHyperParameters(self, matrix, N): 28 | '''''' 29 | # The prior of matrix 30 | beta0 = 2.0 31 | v0 = self.numFactors 32 | mu0 = np.zeros(self.numFactors) 33 | W0 = np.eye(self.numFactors) 34 | 35 | X_bar = matrix.mean(axis=1) 36 | S_bar = np.cov(matrix.transpose()) 37 | 38 | mu0_post = (beta0 * mu0 + N * X_bar) / (beta0 + N) 39 | beta0_post = beta0 + N 40 | v0_post = v0 + N 41 | 42 | WI0 = np.linalg.inv(W0) 43 | WI0_post = WI0 + N * S_bar + beta0 * N / beta0_post * np.outer(mu0 - X_bar, mu0 - X_bar) 44 | W0_post = np.linalg.inv(WI0_post) 45 | 46 | Lambda = wishrnd(W0_post, v0_post) 47 | sigma = np.linalg.cholesky(np.linalg.inv(Lambda * beta0)) 48 | normalRdn = np.random.normal(0, 1, size=self.numFactors) 49 | 50 | mu_post = np.inner(sigma, normalRdn) + mu0_post 51 | 52 | return mu_post, Lambda 53 | 54 | def generateTHyperParameters(self, matrix, K): 55 | pass 56 | 57 | def gibbsUVSampling(self): 58 | pass 59 | 60 | def gibbsTSampling(self): 61 | pass 62 | 63 | def buildModel(self): 64 | pass 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /LDA.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/env python 2 | #coding:utf-8 3 | 4 | ''' 5 | Blei, David M., Andrew Y. Ng, and Michael I. Jordan. "Latent Dirichlet Allocation." 6 | Journal of Machine Learning Research 3 (2003): 993–1022. 7 | ''' 8 | import sys 9 | import numpy as np 10 | 11 | 12 | class LDA: 13 | ''' Latent Dirichlet allocation using collapsed Gibbs sampling ''' 14 | def __init__(self, cf): 15 | self.cf = cf 16 | 17 | def getParameters(self): 18 | self.numFactors = int(self.cf.getParameter('LDA', 'numFactors')) 19 | self.MAX_Iteration = int(self.cf.getParameter('LDA', 'MAX_Iteration')) 20 | 21 | self.alpha = float(self.cf.getParameter('LDA', 'alpha')) 22 | self.beta = float(self.cf.getParameter('LDA', 'beta')) 23 | 24 | 25 | def fit(self): 26 | pass 27 | 28 | 29 | -------------------------------------------------------------------------------- /PMF.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/env python 2 | #coding:utf-8 3 | 4 | from readConfig import ReadConfig 5 | import numpy as np 6 | 7 | 8 | class PMF: 9 | def __init__(self, trainMatrix, testMatrix, cf): 10 | self.trainMatrix = trainMatrix 11 | self.testMatrix = testMatrix 12 | self.cf = cf 13 | self.numUsers = 0 14 | self.numItems = 0 15 | self.predictions = dict() 16 | 17 | def initModel(self): 18 | self.numUsers, self.numItems = self.trainMatrix.shape 19 | self.MAX_Iterations = int(self.cf.getParameter('PMF', 'MAX_Iterations')) 20 | self.numFactors = int(self.cf.getParameter('PMF', 'numFactors')) 21 | self.lRate = float(self.cf.getParameter('PMF', 'rate')) 22 | self.regU = float(self.cf.getParameter('PMF', 'regU')) 23 | self.regI = float(self.cf.getParameter('PMF', 'regI')) 24 | 25 | self.P = np.random.normal(0, 1, size=(self.numUsers, self.numFactors)) 26 | self.Q = np.random.normal(0, 1, size=(self.numItems, self.numFactors)) 27 | 28 | def buildModel(self): 29 | 30 | oldLoss = np.inf 31 | for iteration in range(self.MAX_Iterations): 32 | loss = 0.0 33 | 34 | for u in range(self.numUsers): 35 | for i in range(self.numItems): 36 | rate = self.trainMatrix[u, i] 37 | if rate == 0: 38 | continue 39 | pr_ui = self.predict(u, i) 40 | eui = rate - pr_ui 41 | loss += eui**2 42 | 43 | self.P[u, :] += self.lRate * (eui * self.Q[i, :] - self.regU * self.P[u, :]) 44 | self.Q[i, :] += self.lRate * (eui * self.P[u, :] - self.regI * self.Q[i, :]) 45 | 46 | loss += self.regU * np.sum(self.P[u, :] * self.P[u, :]) + self.regI * np.sum(self.Q[i, :] * self.Q[i, :]) 47 | 48 | if np.abs(oldLoss - loss) < 0.001: 49 | break 50 | else: 51 | oldLoss = loss 52 | 53 | for u in range(self.numUsers): 54 | for i in range(self.numItems): 55 | rate = self.testMatrix[u, i] 56 | if rate == 0: 57 | continue 58 | self.prediction[(u, i)] = self.predict(u, i) 59 | 60 | 61 | def predict(self, u, i): 62 | return np.sum(self.P[u, :] * self.Q[i, :]) 63 | 64 | def evaluate(self, evalString): 65 | metrics = {'rmse': self.rmse, 'mse': self.mse, 'mae': self.mae} 66 | result = metrics[evalString]() 67 | return result 68 | 69 | def rmse(self): 70 | loss = 0 71 | num = 0 72 | for u, i in self.predictions: 73 | error = self.testMatrix[u, i] - self.prediction[(u, i)] 74 | loss += error * error 75 | num += 1 76 | return np.sqrt(loss/num) 77 | 78 | def mse(self): 79 | loss = 0 80 | num = 0 81 | for u, i in self.predictions: 82 | error = self.testMatrix[u, i] - self.prediction[(u, i)] 83 | loss += error * error 84 | num += 1 85 | return loss / num 86 | 87 | def mae(self): 88 | loss = 0 89 | num = 0 90 | for u, i in self.prediction: 91 | error = np.abs(self.testMatrix[u, i] - self.predictions[(u, i)]) 92 | loss += error 93 | num += 1 94 | return loss / num 95 | 96 | 97 | def execute(self): 98 | self.initModel() 99 | self.buildModel() 100 | self.evaluate() 101 | 102 | 103 | 104 | if __name__ == '__main__': 105 | print '__main__' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BPTF 2 | Temporal Collaborative Filtering with Bayesian Probabilistic Tensor Factorization 3 | 4 | This respository will be closed recently, please reference https://github.com/AlgorithmFan/GraphicalModelForRecommendation 5 | 6 | About BPTF, I have some questions, and welcome to communicate with me, thank you. 7 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [BPTF] 2 | MAX_Iteration = 50 3 | N_Sample = 50 4 | numFactors = 30 5 | nuAlpha = 1 6 | Walpha = 1 7 | mu = 0 8 | muT = 1 9 | beta = 1 10 | 11 | 12 | [BPMF] 13 | MAX_Iteration = 50 14 | numFactors = 30 15 | 16 | [LDA] 17 | MAX_Iteration = 50 18 | numFactors = 30 19 | alpha = 0.1 20 | beta = 0.1 21 | -------------------------------------------------------------------------------- /wishart.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions for generating values from Wishart and Inverse-Wishart distributions. 3 | Code taken from http://code.google.com/p/haines/source/browse/gcp/wishart.py. 4 | Author: Herman Kamper 5 | Contact: kamperh@gmail.com 6 | Date: 2013, 2014 7 | """ 8 | 9 | import math 10 | import numpy as np 11 | import random 12 | 13 | 14 | def wishrnd(sigma, v_0, C=None): 15 | """Return a sample from a Wishart distribution.""" 16 | if C == None: 17 | C = np.linalg.cholesky(sigma) 18 | D = sigma.shape[0] 19 | a = np.zeros((D, D), dtype=np.float32) 20 | for r in xrange(D): 21 | if r != 0: 22 | a[r, :r] = np.random.normal(size=(r,)) 23 | a[r, r] = math.sqrt(random.gammavariate(0.5*(v_0 - D + 1), 2.0)) 24 | return np.dot(np.dot(np.dot(C, a), a.T), C.T) 25 | 26 | 27 | def iwishrnd(sigma, v_0, C=None): 28 | """Return a sample from an Inverse-Wishart distribution.""" 29 | sample = wishrnd(sigma, v_0, C); 30 | return np.linalg.solve(sample, np.eye(sample.shape[0])) 31 | 32 | if __name__ == '__main__': 33 | np.random.seed(1) 34 | --------------------------------------------------------------------------------