@Override protected double predict(int u, int j) { // find a number of similar items Map<Integer, Double> nns = new HashMap<>(); SparseVector ui = trainMatrix.row(u); for (int i : ui.getIndex()) { if (itemCorrsmap.get(j).containsKey(i)) { nns.put(i, itemCorrsmap.get(j).get(i)); } } // for (int i : dv.getIndex()) { // double sim = dv.get(i); // //用户u对商品i也购买过 // double rate = trainMatrix.get(u, i); // // if (isRankingPred && rate > 0){ // nns.put(i, sim); // } // else if (sim > 0 && rate > 0) // nns.put(i, sim); // } // topN similar items if (knn > 0 && knn < nns.size()) { List<KeyValPair<Integer>> sorted = Lists.sortMap(nns, true); List<KeyValPair<Integer>> subset = sorted.subList(0, knn); nns.clear(); for (KeyValPair<Integer> kv : subset) nns.put(kv.getKey(), kv.getValue()); } if (nns.size() == 0) return isRankingPred ? 0 : globalMean; if (isRankingPred) { // for recommendation task: item ranking return Stats.sum(nns.values()); } else { // for recommendation task: rating prediction double sum = 0, ws = 0; for (Entry<Integer, Double> en : nns.entrySet()) { int i = en.getKey(); double sim = en.getValue(); double rate = trainMatrix.get(u, i); sum += sim * (rate - itemMeans.get(i)); ws += Math.abs(sim); } return ws > 0 ? itemMeans.get(j) + sum / ws : globalMean; } }
/** * Do vector operation: {@code a * b^t} * * @return the outer product of two vectors */ public DenseMatrix outer(DenseVector vec) { DenseMatrix mat = new DenseMatrix(this.size, vec.size); for (int i = 0; i < mat.numRows; i++) for (int j = 0; j < mat.numColumns; j++) mat.set(i, j, get(i) * vec.get(j)); return mat; }
/** * Do vector operation: {@code a^t * b} * * @return the inner product of two vectors */ public double inner(DenseVector vec) { assert size == vec.size; double result = 0; for (int i = 0; i < vec.size; i++) result += get(i) * vec.get(i); return result; }
/** * Given the similarity, it applies the given kernel. This is done either for all users or for all * items. * * @param size The length of user or item vector. * @param id The identifier of anchor point. * @param kernelType The type of kernel. * @param width Kernel width. * @param isItemFeature return item kernel if yes, return user kernel otherwise. * @return The kernel-smoothed values for all users or all items. */ private DenseVector kernelSmoothing( int size, int id, int kernelType, double width, boolean isItemFeature) { DenseVector newFeatureVector = new DenseVector(size); newFeatureVector.set(id, 1.0); for (int i = 0; i < size; i++) { double sim; if (isItemFeature) { sim = getItemSimilarity(i, id); } else { sim = getUserSimilarity(i, id); } newFeatureVector.set(i, KernelSmoothing.kernelize(sim, width, kernelType)); } return newFeatureVector; }
/** * Calculate similarity between two items, based on the global base SVD. * * @param idx1 The first item's ID. * @param idx2 The second item's ID. * @return The similarity value between two items idx1 and idx2. */ private double getItemSimilarity(int idx1, int idx2) { double sim; DenseVector i_vec = Q.row(idx1); DenseVector j_vec = Q.row(idx2); sim = 1 - 2.0 / Math.PI * Math.acos( i_vec.inner(j_vec) / (Math.sqrt(i_vec.inner(i_vec)) * Math.sqrt(j_vec.inner(j_vec)))); if (Double.isNaN(sim)) { sim = 0.0; } return sim; }
/** * Calculate similarity between two users, based on the global base SVD. * * @param idx1 The first user's ID. * @param idx2 The second user's ID. * @return The similarity value between two users idx1 and idx2. */ private double getUserSimilarity(int idx1, int idx2) { double sim; DenseVector u_vec = P.row(idx1); DenseVector v_vec = P.row(idx2); sim = 1 - 2.0 / Math.PI * Math.acos( u_vec.inner(v_vec) / (Math.sqrt(u_vec.inner(u_vec)) * Math.sqrt(v_vec.inner(v_vec)))); if (Double.isNaN(sim)) { sim = 0.0; } return sim; }
@Override protected void initModel() throws Exception { itemCorrs = buildCorrs(false); itemMeans = new DenseVector(numItems); for (int i = 0; i < numItems; i++) { SparseVector vs = trainMatrix.column(i); itemMeans.set(i, vs.getCount() > 0 ? vs.mean() : globalMean); } for (int i = 0; i < numItems; i++) { SparseVector dv = itemCorrs.row(i); Map<Integer, Double> temp = new HashMap<>(); for (VectorEntry entry : dv) { temp.put(entry.index(), entry.get()); } itemCorrsmap.put(i, temp); } }
@Override protected void buildModel() throws Exception { for (int iter = 1; iter <= numIters; iter++) { // update W by fixing H for (int u = 0; u < W.numRows(); u++) { SparseVector uv = V.row(u); if (uv.getCount() > 0) { SparseVector euv = new SparseVector(V.numColumns()); for (int j : uv.getIndex()) euv.set(j, predict(u, j)); for (int f = 0; f < W.numColumns(); f++) { DenseVector fv = H.row(f, false); double real = fv.inner(uv); double estm = fv.inner(euv) + 1e-9; W.set(u, f, W.get(u, f) * (real / estm)); } } } // update H by fixing W DenseMatrix trW = W.transpose(); for (int j = 0; j < H.numColumns(); j++) { SparseVector jv = V.column(j); if (jv.getCount() > 0) { SparseVector ejv = new SparseVector(V.numRows()); for (int u : jv.getIndex()) ejv.set(u, predict(u, j)); for (int f = 0; f < H.numRows(); f++) { DenseVector fv = trW.row(f, false); double real = fv.inner(jv); double estm = fv.inner(ejv) + 1e-9; H.set(f, j, H.get(f, j) * (real / estm)); } } } // compute errors loss = 0; errs = 0; for (MatrixEntry me : V) { int u = me.row(); int j = me.column(); double ruj = me.get(); if (ruj > 0) { double euj = predict(u, j) - ruj; errs += euj * euj; loss += euj * euj; } } errs *= 0.5; loss *= 0.5; if (isConverged(iter)) break; } }
@Override protected void buildModel() throws Exception { // Initialize hierarchical priors int beta = 2; // observation noise (precision) DenseVector mu_u = new DenseVector(numFactors); DenseVector mu_m = new DenseVector(numFactors); // parameters of Inv-Whishart distribution DenseMatrix WI_u = DenseMatrix.eye(numFactors); int b0_u = 2; int df_u = numFactors; DenseVector mu0_u = new DenseVector(numFactors); DenseMatrix WI_m = DenseMatrix.eye(numFactors); int b0_m = 2; int df_m = numFactors; DenseVector mu0_m = new DenseVector(numFactors); // initializing Bayesian PMF using MAP solution found by PMF P = new DenseMatrix(numUsers, numFactors); Q = new DenseMatrix(numItems, numFactors); P.init(0, 1); Q.init(0, 1); for (int f = 0; f < numFactors; f++) { mu_u.set(f, P.columnMean(f)); mu_m.set(f, Q.columnMean(f)); } DenseMatrix alpha_u = P.cov().inv(); DenseMatrix alpha_m = Q.cov().inv(); // Iteration: DenseVector x_bar = new DenseVector(numFactors); DenseVector normalRdn = new DenseVector(numFactors); DenseMatrix S_bar, WI_post, lam; DenseVector mu_temp; double df_upost, df_mpost; int M = numUsers, N = numItems; for (int iter = 1; iter <= numIters; iter++) { // Sample from user hyper parameters: for (int f = 0; f < numFactors; f++) x_bar.set(f, P.columnMean(f)); S_bar = P.cov(); DenseVector mu0_u_x_bar = mu0_u.minus(x_bar); DenseMatrix e1e2 = mu0_u_x_bar.outer(mu0_u_x_bar).scale(M * b0_u / (b0_u + M + 0.0)); WI_post = WI_u.inv().add(S_bar.scale(M)).add(e1e2); WI_post = WI_post.inv(); WI_post = WI_post.add(WI_post.transpose()).scale(0.5); df_upost = df_u + M; DenseMatrix wishrnd_u = wishart(WI_post, df_upost); if (wishrnd_u != null) alpha_u = wishrnd_u; mu_temp = mu0_u.scale(b0_u).add(x_bar.scale(M)).scale(1 / (b0_u + M + 0.0)); lam = alpha_u.scale(b0_u + M).inv().cholesky(); if (lam != null) { lam = lam.transpose(); for (int f = 0; f < numFactors; f++) normalRdn.set(f, Randoms.gaussian(0, 1)); mu_u = lam.mult(normalRdn).add(mu_temp); } // Sample from item hyper parameters: for (int f = 0; f < numFactors; f++) x_bar.set(f, Q.columnMean(f)); S_bar = Q.cov(); DenseVector mu0_m_x_bar = mu0_m.minus(x_bar); DenseMatrix e3e4 = mu0_m_x_bar.outer(mu0_m_x_bar).scale(N * b0_m / (b0_m + N + 0.0)); WI_post = WI_m.inv().add(S_bar.scale(N)).add(e3e4); WI_post = WI_post.inv(); WI_post = WI_post.add(WI_post.transpose()).scale(0.5); df_mpost = df_m + N; DenseMatrix wishrnd_m = wishart(WI_post, df_mpost); if (wishrnd_m != null) alpha_m = wishrnd_m; mu_temp = mu0_m.scale(b0_m).add(x_bar.scale(N)).scale(1 / (b0_m + N + 0.0)); lam = alpha_m.scale(b0_m + N).inv().cholesky(); if (lam != null) { lam = lam.transpose(); for (int f = 0; f < numFactors; f++) normalRdn.set(f, Randoms.gaussian(0, 1)); mu_m = lam.mult(normalRdn).add(mu_temp); } // Gibbs updates over user and item feature vectors given hyper parameters: // NOTE: in PREA, only 1 iter for gibbs where in the original Matlab code, 2 iters are used. for (int gibbs = 0; gibbs < 2; gibbs++) { // Infer posterior distribution over all user feature vectors for (int u = 0; u < numUsers; u++) { // list of items rated by user uu: SparseVector rv = trainMatrix.row(u); int count = rv.getCount(); if (count == 0) continue; // features of items rated by user uu: DenseMatrix MM = new DenseMatrix(count, numFactors); DenseVector rr = new DenseVector(count); int idx = 0; for (int j : rv.getIndex()) { rr.set(idx, rv.get(j) - globalMean); for (int f = 0; f < numFactors; f++) MM.set(idx, f, Q.get(j, f)); idx++; } DenseMatrix covar = alpha_u.add((MM.transpose().mult(MM)).scale(beta)).inv(); DenseVector a = MM.transpose().mult(rr).scale(beta); DenseVector b = alpha_u.mult(mu_u); DenseVector mean_u = covar.mult(a.add(b)); lam = covar.cholesky(); if (lam != null) { lam = lam.transpose(); for (int f = 0; f < numFactors; f++) normalRdn.set(f, Randoms.gaussian(0, 1)); DenseVector w1_P1_u = lam.mult(normalRdn).add(mean_u); for (int f = 0; f < numFactors; f++) P.set(u, f, w1_P1_u.get(f)); } } // Infer posterior distribution over all movie feature vectors for (int j = 0; j < numItems; j++) { // list of users who rated item ii: SparseVector jv = trainMatrix.column(j); int count = jv.getCount(); if (count == 0) continue; // features of users who rated item ii: DenseMatrix MM = new DenseMatrix(count, numFactors); DenseVector rr = new DenseVector(count); int idx = 0; for (int u : jv.getIndex()) { rr.set(idx, jv.get(u) - globalMean); for (int f = 0; f < numFactors; f++) MM.set(idx, f, P.get(u, f)); idx++; } DenseMatrix covar = alpha_m.add((MM.transpose().mult(MM)).scale(beta)).inv(); DenseVector a = MM.transpose().mult(rr).scale(beta); DenseVector b = alpha_m.mult(mu_m); DenseVector mean_m = covar.mult(a.add(b)); lam = covar.cholesky(); if (lam != null) { lam = lam.transpose(); for (int f = 0; f < numFactors; f++) normalRdn.set(f, Randoms.gaussian(0, 1)); DenseVector w1_M1_j = lam.mult(normalRdn).add(mean_m); for (int f = 0; f < numFactors; f++) Q.set(j, f, w1_M1_j.get(f)); } } } // end of gibbs loss = 0; for (MatrixEntry me : trainMatrix) { int u = me.row(); int j = me.column(); double ruj = me.get(); double pred = predict(u, j); double euj = ruj - pred; loss += euj * euj; } loss *= 0.5; if (isConverged(iter)) break; } }