/// public virtual void Iterate() { if (num_learned_factors >= NumFactors) { return; } // compute residuals for (int index = 0; index < Ratings.Count; index++) { int u = Ratings.Users[index]; int i = Ratings.Items[index]; residuals[index] = Ratings[index] - Predict(u, i); int n_ui = Math.Min(Ratings.ByUser[u].Count, Ratings.ByItem[i].Count); // TODO use less memory residuals[index] *= n_ui / (n_ui + Shrinkage); } // initialize new latent factors MatrixUtils.ColumnInitNormal(user_factors, InitMean, InitStdev, num_learned_factors); MatrixUtils.ColumnInitNormal(item_factors, InitMean, InitStdev, num_learned_factors); // TODO make configurable? // compute the next factor by solving many least squares problems with one variable each double err = double.MaxValue / 2; double err_old = double.MaxValue; while (err / err_old < 1 - Sensibility) { { // TODO create only once? var user_factors_update_numerator = new double[MaxUserID + 1]; var user_factors_update_denominator = new double[MaxUserID + 1]; // compute updates in one pass over the data for (int index = 0; index < Ratings.Count; index++) { int u = Ratings.Users[index]; int i = Ratings.Items[index]; user_factors_update_numerator[u] += residuals[index] * item_factors[i, num_learned_factors]; user_factors_update_denominator[u] += item_factors[i, num_learned_factors] * item_factors[i, num_learned_factors]; } // update user factors for (int u = 0; u <= MaxUserID; u++) { if (user_factors_update_numerator[u] != 0) { user_factors[u, num_learned_factors] = user_factors_update_numerator[u] / user_factors_update_denominator[u]; } } } { var item_factors_update_numerator = new double[MaxItemID + 1]; var item_factors_update_denominator = new double[MaxItemID + 1]; // compute updates in one pass over the data for (int index = 0; index < Ratings.Count; index++) { int u = Ratings.Users[index]; int i = Ratings.Items[index]; item_factors_update_numerator[i] += residuals[index] * user_factors[u, num_learned_factors]; item_factors_update_denominator[i] += user_factors[u, num_learned_factors] * user_factors[u, num_learned_factors]; } // update item factors for (int i = 0; i <= MaxItemID; i++) { if (item_factors_update_numerator[i] != 0) { item_factors[i, num_learned_factors] = item_factors_update_numerator[i] / item_factors_update_denominator[i]; } } } err_old = err; err = ComputeFit(); } num_learned_factors++; }