/** * 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; }
@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--; } } } }