Ejemplo n.º 1
0
        public MfModel Fit(MfProblem trainingData, MfProblem testing, MfTrainerOptions mfTrainerOptions)
        {
            var matrix = SparseMatrix.CreateFromMfProblem(trainingData);

            var W = InitializeColumn(mfTrainerOptions.ApproximationRank, matrix.Rows);
            var H = InitializeColumn(mfTrainerOptions.ApproximationRank, matrix.Cols);

            var watcher = new Stopwatch();

            watcher.Start();
            CoordinateDescentCore(matrix, W, H, testing, mfTrainerOptions);
            watcher.Stop();
            _logger?.WriteLine($"Time taken is {watcher.ElapsedMilliseconds} ms.");
            return(new MfModel()
            {
                M = matrix.Rows,
                N = matrix.Cols,
                K = mfTrainerOptions.ApproximationRank,
                W = W,
                H = H
            });
        }
Ejemplo n.º 2
0
        // Cyclic Coordinate Descent for Matrix Factorization
        private void CoordinateDescentCore(SparseMatrix r, float[][] w, float[][] h,
                                           MfProblem testProblem, MfTrainerOptions options)
        {
            long  k = options.ApproximationRank;
            long  numberOfIterations = options.NumberOfIterations;
            long  innerIterations    = options.NumberOfInnerIterations;
            var   numberOfThread     = options.NumberOfThreads;
            var   lambda             = options.LambdaRegularization;
            var   eps = options.Eps;
            float wTime = 0, hTime = 0, rTime = 0;
            var   doNmf           = options.NonNegativeMatrixFactorization;
            var   verbose         = options.Verbose;
            var   parallelOptions = new ParallelOptions()
            {
                MaxDegreeOfParallelism = numberOfThread
            };

            // Create transpose view of R
            var rt        = r.Transpose();
            var stopwatch = new Stopwatch();

            // initial value of the regularization term
            // H is a zero matrix now.
            for (long feature = 0; feature < k; ++feature)
            {
                for (long column = 0; column < r.Cols; ++column)
                {
                    h[feature][column] = 0;
                }
            }

            var oldWt = new float[r.Rows];
            var oldHt = new float[r.Cols];
            var u     = new float[r.Rows];
            var v     = new float[r.Cols];

            for (long outerIteration = 1; outerIteration <= numberOfIterations; ++outerIteration)
            {
                float fundecMax = 0;
                long  earlyStop = 0;
                for (long tt = 0; tt < k; ++tt)
                {
                    long t = tt;
                    if (earlyStop >= 5)
                    {
                        break;
                    }

                    stopwatch.Start();

                    float[] wt = w[t], ht = h[t];
                    for (int i = 0; i < r.Rows; i++)
                    {
                        oldWt[i] = u[i] = wt[i];
                    }

                    for (int i = 0; i < r.Cols; i++)
                    {
                        v[i]     = ht[i];
                        oldHt[i] = (outerIteration == 1) ? 0 : v[i];
                    }

                    // Create Rhat = R - Wt Ht^T
                    if (outerIteration > 1)
                    {
                        UpdateRating(r, wt, ht, true, parallelOptions);
                        UpdateRating(rt, ht, wt, true, parallelOptions);
                    }

                    stopwatch.Stop();

                    double innerFundecMax = 0;
                    long   maxIterations  = innerIterations;
                    //	if(oiter > 1) maxit *= 2;
                    for (long iteration = 1; iteration <= maxIterations; ++iteration)
                    {
                        // Update H[t]
                        stopwatch.Restart();
                        var innerFunDecCur = 0f;

                        var innerFun = new ThreadSafe();

                        Parallel.For(0, r.Cols,
                                     parallelOptions, () => 0f,
                                     (c, y, z) =>
                        {
                            v[c] = RankOneUpdate(r, c, u,
                                                 (lambda * (r.ColPtr[c + 1] - r.ColPtr[c])),
                                                 v[c], doNmf, ref z);
                            ;
                            return(z);
                        }, f =>
                        {
                            lock (innerFun)
                            {
                                innerFun.AddToTotal(f);
                            }
                        });

                        stopwatch.Stop();
                        hTime += stopwatch.ElapsedMilliseconds;
                        // Update W[t]
                        stopwatch.Restart();

                        Parallel.For(0, rt.Cols,
                                     parallelOptions, () => 0f,
                                     (c, y, z) =>
                        {
                            u[c] = RankOneUpdate(rt, c, v,
                                                 (lambda * (rt.ColPtr[c + 1] - rt.ColPtr[c])), u[c], doNmf, ref z);
                            return(z);
                        }, f =>
                        {
                            lock (innerFun)
                            {
                                innerFun.AddToTotal(f);
                            }
                        });

                        innerFunDecCur += innerFun.Total;

                        if ((innerFunDecCur < fundecMax * eps))
                        {
                            if (iteration == 1)
                            {
                                earlyStop += 1;
                            }

                            break;
                        }

                        innerFundecMax = Math.Max(innerFundecMax, innerFunDecCur);
                        // the fundec of the first inner iter of the first rank of the first outer iteration could be too large!!
                        if (!(outerIteration == 1 && t == 0 && iteration == 1))
                        {
                            fundecMax = Math.Max(fundecMax, innerFunDecCur);
                        }
                        stopwatch.Stop();
                        wTime += stopwatch.ElapsedMilliseconds;
                    }

                    // Update R and Rt
                    // start = omp_get_wtime();
                    stopwatch.Restart();

                    for (int i = 0; i < r.Rows; i++)
                    {
                        wt[i] = u[i];
                    }

                    for (int i = 0; i < r.Cols; i++)
                    {
                        ht[i] = v[i];
                    }

                    UpdateRating(r, u, v, false, parallelOptions);
                    UpdateRating(rt, v, u, false, parallelOptions);

                    stopwatch.Stop();
                    rTime += stopwatch.ElapsedMilliseconds;
                }

                if (testProblem != null && verbose)
                {
                    _logger?.Write("iter {0, 5} time {1, 5} rmse {2, 5}", outerIteration,
                                   hTime + wTime + rTime, testProblem.CalculateRmseOneRow(w, h, k));
                }
            }
        }