13 #ifndef MLPACK_METHODS_SVDPLUSPLUS_SVDPLUSPLUS_FUNCTION_IMPL_HPP 14 #define MLPACK_METHODS_SVDPLUSPLUS_SVDPLUSPLUS_FUNCTION_IMPL_HPP 22 template <
typename MatType>
25 const arma::sp_mat& implicitData,
27 const double lambda) :
28 data(math::
MakeAlias(const_cast<MatType&>(data), false)),
29 implicitData(implicitData),
34 numUsers = max(data.row(0)) + 1;
35 numItems = max(data.row(1)) + 1;
46 initialPoint.randu(rank + 1, numUsers + 2 * numItems);
49 template<
typename MatType>
52 data =
data.cols(arma::shuffle(arma::linspace<arma::uvec>(0,
data.n_cols - 1,
56 template <
typename MatType>
62 template <
typename MatType>
65 const size_t batchSize)
const 78 double objective = 0.0;
82 arma::vec implicitVecsNormSquare(numItems);
83 implicitVecsNormSquare.fill(-1);
85 for (
size_t i = start; i < start + batchSize; ++i)
88 const size_t user =
data(0, i);
89 const size_t item =
data(1, i) + numUsers;
90 const size_t implicitStart = numUsers + numItems;
93 const double rating =
data(2, i);
94 const double userBias = parameters(rank, user);
95 const double itemBias = parameters(rank, item);
99 arma::vec userVec(rank, arma::fill::zeros);
100 arma::sp_mat::const_iterator it = implicitData.begin_col(user);
101 arma::sp_mat::const_iterator it_end = implicitData.end_col(user);
102 size_t implicitCount = 0;
103 double regularizationError = 0;
104 for (; it != it_end; ++it)
106 userVec += parameters.col(implicitStart + it.row()).subvec(0, rank - 1);
107 if (implicitVecsNormSquare(it.row()) < 0)
109 implicitVecsNormSquare(it.row()) = arma::dot(
110 parameters.col(implicitStart + it.row()).subvec(0, rank - 1),
111 parameters.col(implicitStart + it.row()).subvec(0, rank - 1));
113 regularizationError += lambda * implicitVecsNormSquare(it.row());
116 if (implicitCount != 0)
118 userVec /= std::sqrt(implicitCount);
119 regularizationError /= implicitCount;
121 userVec += parameters.col(user).subvec(0, rank - 1);
123 double ratingError = rating - userBias - itemBias -
124 arma::dot(userVec, parameters.col(item).subvec(0, rank - 1));
125 double ratingErrorSquared = ratingError * ratingError;
128 double userVecNorm = arma::norm(parameters.col(user), 2);
129 double itemVecNorm = arma::norm(parameters.col(item), 2);
130 regularizationError += lambda * (userVecNorm * userVecNorm +
131 itemVecNorm * itemVecNorm);
133 objective += (ratingErrorSquared + regularizationError);
139 template <
typename MatType>
141 arma::mat& gradient)
const 156 gradient.zeros(rank + 1, numUsers + 2 * numItems);
158 for (
size_t i = 0; i <
data.n_cols; ++i)
161 const size_t user =
data(0, i);
162 const size_t item =
data(1, i) + numUsers;
163 const size_t implicitStart = numUsers + numItems;
166 const double rating =
data(2, i);
167 const double userBias = parameters(rank, user);
168 const double itemBias = parameters(rank, item);
172 arma::vec userVec(rank, arma::fill::zeros);
173 arma::sp_mat::const_iterator it = implicitData.begin_col(user);
174 arma::sp_mat::const_iterator it_end = implicitData.end_col(user);
175 size_t implicitCount = 0;
176 for (; it != it_end; ++it)
178 userVec += parameters.col(implicitStart + it.row()).subvec(0, rank - 1);
181 if (implicitCount != 0)
182 userVec /= std::sqrt(implicitCount);
183 userVec += parameters.col(user).subvec(0, rank - 1);
185 double ratingError = rating - userBias - itemBias -
186 arma::dot(userVec, parameters.col(item).subvec(0, rank - 1));
190 gradient.col(user).subvec(0, rank - 1) +=
191 2 * (lambda * parameters.col(user).subvec(0, rank - 1) -
192 ratingError * parameters.col(item).subvec(0, rank - 1));
193 gradient.col(item).subvec(0, rank - 1) +=
194 2 * (lambda * parameters.col(item).subvec(0, rank - 1) -
195 ratingError * userVec);
196 gradient(rank, user) +=
197 2 * (lambda * parameters(rank, user) - ratingError);
198 gradient(rank, item) +=
199 2 * (lambda * parameters(rank, item) - ratingError);
201 it = implicitData.begin_col(user);
202 it_end = implicitData.end_col(user);
203 for (; it != it_end; ++it)
206 gradient.col(implicitStart + it.row()).subvec(0, rank - 1) +=
207 2.0 * (lambda / implicitCount *
208 parameters.col(implicitStart + it.row()).subvec(0, rank - 1) -
209 ratingError / std::sqrt(implicitCount) *
210 parameters.col(item).subvec(0, rank - 1));
215 template <
typename MatType>
216 template <
typename GradType>
220 const size_t batchSize)
const 222 gradient.zeros(rank + 1, numUsers + 2 * numItems);
225 for (
size_t i = start; i < start + batchSize; ++i)
228 const size_t user =
data(0, i);
229 const size_t item =
data(1, i) + numUsers;
230 const size_t implicitStart = numUsers + numItems;
233 const double rating =
data(2, i);
234 const double userBias = parameters(rank, user);
235 const double itemBias = parameters(rank, item);
239 arma::vec userVec(rank, arma::fill::zeros);
240 arma::sp_mat::const_iterator it = implicitData.begin_col(user);
241 arma::sp_mat::const_iterator it_end = implicitData.end_col(user);
242 size_t implicitCount = 0;
243 for (; it != it_end; ++it)
245 userVec += parameters.col(implicitStart + it.row()).subvec(0, rank - 1);
248 if (implicitCount != 0)
249 userVec /= std::sqrt(implicitCount);
250 userVec += parameters.col(user).subvec(0, rank - 1);
252 double ratingError = rating - userBias - itemBias -
253 arma::dot(userVec, parameters.col(item).subvec(0, rank - 1));
257 for (
size_t j = 0; j < rank; ++j)
260 2 * (lambda * parameters(j, user) -
261 ratingError * parameters(j, item));
263 2 * (lambda * parameters(j, item) -
264 ratingError * userVec(j));
266 gradient(rank, user) +=
267 2 * (lambda * parameters(rank, user) - ratingError);
268 gradient(rank, item) +=
269 2 * (lambda * parameters(rank, item) - ratingError);
271 it = implicitData.begin_col(user);
272 it_end = implicitData.end_col(user);
273 for (; it != it_end; ++it)
276 for (
size_t j = 0; j < rank; ++j)
278 gradient(j, implicitStart + it.row()) +=
279 2.0 * (lambda / implicitCount *
280 parameters(j, implicitStart + it.row()) -
281 ratingError / std::sqrt(implicitCount) *
282 parameters(j, item));
296 double StandardSGD::Optimize(
298 arma::mat& parameters)
301 const size_t numFunctions =
function.NumFunctions();
304 size_t currentFunction = 0;
305 double overallObjective = 0;
308 for (
size_t i = 0; i < numFunctions; ++i)
309 overallObjective +=
function.
Evaluate(parameters, i);
311 const arma::mat
data =
function.Dataset();
312 const arma::sp_mat implicitData =
function.ImplicitDataset();
313 const size_t numUsers =
function.NumUsers();
314 const size_t numItems =
function.NumItems();
315 const double lambda =
function.Lambda();
318 const size_t rank =
function.Rank();
321 for (
size_t i = 1; i != maxIterations; ++i, currentFunction++)
324 if ((currentFunction % numFunctions) == 0)
326 const size_t epoch = i / numFunctions + 1;
328 << overallObjective <<
"." << std::endl;
331 overallObjective = 0;
336 const size_t user = data(0, currentFunction);
337 const size_t item = data(1, currentFunction) + numUsers;
338 const size_t implicitStart = numUsers + numItems;
341 const double rating = data(2, currentFunction);
342 const double userBias = parameters(rank, user);
343 const double itemBias = parameters(rank, item);
347 arma::vec userVec(rank, arma::fill::zeros);
348 arma::sp_mat::const_iterator it = implicitData.begin_col(user);
349 arma::sp_mat::const_iterator it_end = implicitData.end_col(user);
350 size_t implicitCount = 0;
351 for (; it != it_end; ++it)
353 userVec += parameters.col(implicitStart + it.row()).subvec(0, rank - 1);
356 if (implicitCount != 0)
357 userVec /= std::sqrt(implicitCount);
358 userVec += parameters.col(user).subvec(0, rank - 1);
360 double ratingError = rating - userBias - itemBias -
361 arma::dot(userVec, parameters.col(item).subvec(0, rank - 1));
365 parameters.col(user).subvec(0, rank - 1) -= stepSize * 2 * (
366 lambda * parameters.col(user).subvec(0, rank - 1) -
367 ratingError * parameters.col(item).subvec(0, rank - 1));
368 parameters.col(item).subvec(0, rank - 1) -= stepSize * 2 * (
369 lambda * parameters.col(item).subvec(0, rank - 1) -
370 ratingError * userVec);
371 parameters(rank, user) -= stepSize * 2 * (
372 lambda * parameters(rank, user) - ratingError);
373 parameters(rank, item) -= stepSize * 2 * (
374 lambda * parameters(rank, item) - ratingError);
376 it = implicitData.begin_col(user);
377 it_end = implicitData.end_col(user);
378 for (; it != it_end; ++it)
381 parameters.col(implicitStart + it.row()).subvec(0, rank - 1) -=
382 stepSize * 2.0 * (lambda / implicitCount *
383 parameters.col(implicitStart + it.row()).subvec(0, rank - 1) -
384 ratingError / std::sqrt(implicitCount) *
385 parameters.col(item).subvec(0, rank - 1));
389 overallObjective +=
function.Evaluate(parameters, currentFunction);
392 return overallObjective;
398 inline double ParallelSGD<ExponentialBackoff>::Optimize(
402 double overallObjective = DBL_MAX;
403 double lastObjective;
406 arma::Col<size_t> visitationOrder = arma::linspace<arma::Col<size_t>>(0,
407 (
function.NumFunctions() - 1),
function.
NumFunctions());
409 const arma::mat
data =
function.Dataset();
410 const arma::sp_mat implicitData =
function.ImplicitDataset();
411 const size_t numUsers =
function.NumUsers();
412 const size_t numItems =
function.NumItems();
413 const double lambda =
function.Lambda();
416 const size_t rank =
function.Rank();
421 for (
size_t i = 1; i != maxIterations; ++i)
424 lastObjective = overallObjective;
425 overallObjective = 0;
427 #pragma omp parallel for reduction(+:overallObjective) 428 for (omp_size_t j = 0; j < (omp_size_t)
function.
NumFunctions(); ++j)
430 overallObjective +=
function.Evaluate(iterate, j);
435 << overallObjective <<
"." << std::endl;
437 if (std::isnan(overallObjective) || std::isinf(overallObjective))
440 <<
"; terminating with failure. Try a smaller step size?" 442 return overallObjective;
445 if (std::abs(lastObjective - overallObjective) < tolerance)
448 <<
"; terminating optimization." << std::endl;
449 return overallObjective;
453 double stepSize = decayPolicy.StepSize(i);
456 std::shuffle(visitationOrder.begin(), visitationOrder.end(),
465 threadId = omp_get_thread_num();
468 for (
size_t j = threadId * threadShareSize;
469 j < (threadId + 1) * threadShareSize && j < visitationOrder.n_elem;
473 const size_t user = data(0, visitationOrder[j]);
474 const size_t item = data(1, visitationOrder[j]) + numUsers;
475 const size_t implicitStart = numUsers + numItems;
478 const double rating = data(2, visitationOrder[j]);
479 const double userBias = iterate(rank, user);
480 const double itemBias = iterate(rank, item);
483 arma::vec userVec(rank, arma::fill::zeros);
484 arma::sp_mat::const_iterator it = implicitData.begin_col(user);
485 arma::sp_mat::const_iterator it_end = implicitData.end_col(user);
486 size_t implicitCount = 0;
487 for (; it != it_end; ++it)
489 userVec += iterate.col(implicitStart + it.row()).subvec(0, rank - 1);
492 if (implicitCount != 0)
493 userVec /= std::sqrt(implicitCount);
494 userVec += iterate.col(user).subvec(0, rank - 1);
496 double ratingError = rating - userBias - itemBias -
497 arma::dot(userVec, iterate.col(item).subvec(0, rank - 1));
499 arma::mat userVecUpdate = stepSize * 2 * (
500 lambda * iterate.col(user).subvec(0, rank - 1) -
501 ratingError * iterate.col(item).subvec(0, rank - 1));
502 arma::mat itemVecUpdate = stepSize * 2 * (
503 lambda * iterate.col(item).subvec(0, rank - 1) -
504 ratingError * userVec);
505 double userBiasUpdate = stepSize * 2 * (
506 lambda * iterate(rank, user) - ratingError);
507 double itemBiasUpdate = stepSize * 2 * (
508 lambda * iterate(rank, item) - ratingError);
511 arma::mat itemImplicitUpdate(rank, implicitCount);
512 arma::Col<size_t> implicitItems(implicitCount);
513 it = implicitData.begin_col(user);
514 it_end = implicitData.end_col(user);
515 size_t implicitIndex = 0;
516 for (; it != it_end; ++it, ++implicitIndex)
518 itemImplicitUpdate.col(implicitIndex) =
519 stepSize * 2.0 * (lambda / implicitCount *
520 iterate.col(implicitStart + it.row()).subvec(0, rank - 1) -
521 ratingError / std::sqrt(implicitCount) *
522 iterate.col(item).subvec(0, rank - 1));
523 implicitItems(implicitIndex) = it.row();
528 for (
size_t i = 0; i < rank; ++i)
531 iterate(i, user) -= userVecUpdate(i);
533 iterate(i, item) -= itemVecUpdate(i);
536 iterate(rank, user) -= userBiasUpdate;
538 iterate(rank, item) -= itemBiasUpdate;
539 for (
size_t k = 0; k < implicitCount; ++k)
541 for (
size_t i = 0; i < rank; ++i)
544 iterate(i, implicitStart + implicitItems(k)) -=
545 itemImplicitUpdate(i, k);
552 << overallObjective << std::endl;
554 return overallObjective;
This class contains methods which are used to calculate the cost of SVD++'s objective function...
Definition: svdplusplus_function.hpp:31
void Gradient(const arma::mat ¶meters, arma::mat &gradient) const
Evaluates the full gradient of the cost function over all the training examples.
Definition: svdplusplus_function_impl.hpp:140
Linear algebra utility functions, generally performed on matrices or vectors.
Definition: cv.hpp:1
Definition: bias_svd_function_impl.hpp:185
SVDPlusPlusFunction(const MatType &data, const arma::sp_mat &implicitData, const size_t rank, const double lambda)
Constructor for SVDPlusPlusFunction class.
Definition: svdplusplus_function_impl.hpp:23
static MLPACK_EXPORT util::PrefixedOutStream Warn
Prints warning messages prefixed with [WARN ].
Definition: log.hpp:87
void Shuffle()
Shuffle the points in the dataset.
Definition: svdplusplus_function_impl.hpp:50
size_t NumFunctions() const
Return the number of training examples. Useful for SGD optimizer.
Definition: svdplusplus_function.hpp:116
double Evaluate(const arma::mat ¶meters) const
Evaluates the cost function over all examples in the data.
Definition: svdplusplus_function_impl.hpp:57
static MLPACK_EXPORT util::PrefixedOutStream Info
Prints informational messages if –verbose is specified, prefixed with [INFO ].
Definition: log.hpp:84
arma::Cube< ElemType > MakeAlias(arma::Cube< ElemType > &input, const bool strict=true)
Make an alias of a dense cube.
Definition: make_alias.hpp:24
MLPACK_EXPORT std::mt19937 randGen
MLPACK_EXPORT is required for global variables; it exports the symbols correctly on Windows...
Definition: random.cpp:18