/** @return the transpose of current matrix */ public SparseMatrix transpose() { if (isCCSUsed) { SparseMatrix tr = new SparseMatrix(numColumns, numRows); tr.copyCRS(this.rowData, this.rowPtr, this.colInd); tr.copyCCS(this.colData, this.colPtr, this.rowInd); return tr; } else { Table<Integer, Integer, Double> dataTable = HashBasedTable.create(); for (MatrixEntry me : this) dataTable.put(me.column(), me.row(), me.get()); return new SparseMatrix(numColumns, numRows, dataTable); } }
@Override protected void initModel() throws Exception { testIndexMatrix = new SparseMatrix(testMatrix); for (MatrixEntry me : testIndexMatrix) { int u = me.row(); int i = me.column(); testIndexMatrix.set(u, i, 0.0); } // global svd P Q to calculate the kernel value between users (or items) P = new DenseMatrix(numUsers, numFactors); Q = new DenseMatrix(numItems, numFactors); // initialize model if (initByNorm) { P.init(initMean, initStd); Q.init(initMean, initStd); } else { P.init(); // P.init(smallValue); Q.init(); // Q.init(smallValue); } this.buildGlobalModel(); }
@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 double predict(int u, int i) throws Exception { return predictMatrix.get(u, i); }
@Override protected void buildModel() throws Exception { // Pre-calculating similarity: int completeModelCount = 0; LLORMAUpdater[] learners = new LLORMAUpdater[multiThreadCount]; int[] anchorUser = new int[modelMax]; int[] anchorItem = new int[modelMax]; int modelCount = 0; int[] runningThreadList = new int[multiThreadCount]; int runningThreadCount = 0; int waitingThreadPointer = 0; int nextRunningSlot = 0; cumPrediction = new SparseMatrix(testIndexMatrix); cumWeight = new SparseMatrix(testIndexMatrix); // Parallel training: while (completeModelCount < modelMax) { int u_t = (int) Math.floor(Math.random() * numUsers); List<Integer> itemList = trainMatrix.getColumns(u_t); if (itemList != null) { if (runningThreadCount < multiThreadCount && modelCount < modelMax) { // Selecting a new anchor point: int idx = (int) Math.floor(Math.random() * itemList.size()); int i_t = itemList.get(idx); anchorUser[modelCount] = u_t; anchorItem[modelCount] = i_t; // Preparing weight vectors: DenseVector w = kernelSmoothing(numUsers, u_t, KernelSmoothing.EPANECHNIKOV_KERNEL, 0.8, false); DenseVector v = kernelSmoothing(numItems, i_t, KernelSmoothing.EPANECHNIKOV_KERNEL, 0.8, true); // Starting a new local model learning: learners[nextRunningSlot] = new LLORMAUpdater( modelCount, localNumFactors, numUsers, numItems, u_t, i_t, localLRate, localRegU, localRegI, localNumIters, w, v, trainMatrix); learners[nextRunningSlot].start(); runningThreadList[runningThreadCount] = modelCount; runningThreadCount++; modelCount++; nextRunningSlot++; } else if (runningThreadCount > 0) { // Joining a local model which was done with learning: try { learners[waitingThreadPointer].join(); } catch (InterruptedException ie) { System.out.println("Join failed: " + ie); } int mp = waitingThreadPointer; int mc = completeModelCount; completeModelCount++; // Predicting with the new local model and all previous // models: predictMatrix = new SparseMatrix(testIndexMatrix); for (MatrixEntry me : testMatrix) { int u = me.row(); int i = me.column(); double weight = KernelSmoothing.kernelize( getUserSimilarity(anchorUser[mc], u), 0.8, KernelSmoothing.EPANECHNIKOV_KERNEL) * KernelSmoothing.kernelize( getItemSimilarity(anchorItem[mc], i), 0.8, KernelSmoothing.EPANECHNIKOV_KERNEL); double newPrediction = (learners[mp].getUserFeatures().row(u).inner(learners[mp].getItemFeatures().row(i))) * weight; cumWeight.set(u, i, cumWeight.get(u, i) + weight); cumPrediction.set(u, i, cumPrediction.get(u, i) + newPrediction); double prediction = cumPrediction.get(u, i) / cumWeight.get(u, i); if (Double.isNaN(prediction) || prediction == 0.0) { prediction = globalMean; } if (prediction < minRate) { prediction = minRate; } else if (prediction > maxRate) { prediction = maxRate; } predictMatrix.set(u, i, prediction); } if (completeModelCount % 5 == 0) { Logs.debug( "{}{} iter {}:[MAE,RMSE,NMAE,rMAE,rRMSE,MPE] {}", algoName, foldInfo, completeModelCount, "[" + Recommender.getEvalInfo(evalRatings()) + "]"); } nextRunningSlot = waitingThreadPointer; waitingThreadPointer = (waitingThreadPointer + 1) % multiThreadCount; runningThreadCount--; } } } }