NaN 通过矩阵分解
Posted
技术标签:
【中文标题】NaN 通过矩阵分解【英文标题】:NaN by Matrix Factorization 【发布时间】:2020-05-14 15:59:14 【问题描述】:我使用 SGD 算法实现了矩阵分解,但是当我运行它时,我经常在预测矩阵中得到 NaN。当我在一个非常小的 (6 x 7) 矩阵上运行算法时,错误出现的次数很少。当我转移到电影镜头数据集时,每次运行算法时,我都会在所有单元格中得到错误。错误仅在某些单元格中消失的唯一时间是当我将优化步骤(迭代次数)设置为 1 时。
private static Matrix matrixFactorizationLarge (Matrix realRatingMatrix, Matrix factor_1, Matrix factor_2)
int features = (int) factor_1.getColumnCount();
double learningRate = 0.02;
double regularization = 0.02;
int optimizationSteps = 10;
Matrix predictedRatingMatrix = SparseMatrix.Factory.zeros(realRatingMatrix.getRowCount(), realRatingMatrix.getColumnCount());
for (int step = 0; step < optimizationSteps; step++)
for (int row = 0; row < predictedRatingMatrix.getRowCount(); row++)
for (int col = 0; col < predictedRatingMatrix.getColumnCount(); col++)
if (realRatingMatrix.getAsInt(row, col) > 0)
Matrix vector_1 = getRow(factor_1, row);
Matrix vector_2 = getColumn(factor_2, col);
predictedRatingMatrix.setAsDouble( ( Math.floor ( dotProduct(vector_1, vector_2) * 100 ) ) / 100, row, col);
for (int f = 0; f < features; f++)
factor_1.setAsDouble( ( Math.floor ( ( factor_1.getAsDouble(row, f) + ( learningRate * ( ( calculateDerivative(realRatingMatrix.getAsDouble(row, col), predictedRatingMatrix.getAsDouble(row, col), factor_2.getAsDouble(f, col) ) ) - ( regularization * factor_1.getAsDouble(row, f) ) ) ) ) * 100 ) / 100), row, f);
factor_2.setAsDouble( ( Math.floor ( ( factor_2.getAsDouble(f, col) + ( learningRate * ( ( calculateDerivative(realRatingMatrix.getAsDouble(row, col), predictedRatingMatrix.getAsDouble(row, col), factor_1.getAsDouble(row, f) ) ) - ( regularization * factor_2.getAsDouble(f, col) ) ) ) ) * 100 ) / 100), f, col);
return predictedRatingMatrix;
相关方法如下:
private static double dotProduct (Matrix vector_A, Matrix vector_B)
double dotProduct = 0.0;
for (int index = 0; index < vector_A.getColumnCount(); index++)
dotProduct = dotProduct + ( vector_A.getAsDouble(0, index) * vector_B.getAsDouble(0, index) );
return dotProduct;
private static double errorOfDotProduct (double original, double dotProduct)
double error = 0.0;
error = Math.pow( ( original - dotProduct ), 2 );
return error;
private static double calculateDerivative(double realValue, double predictedValue, double value)
return ( 2 * (realValue - predictedValue) * (value) );
private static double calculateRMSE (Matrix realRatingMatrix, Matrix predictedRatingMatrix)
double rmse = 0.0;
double summation = 0.0;
for (int row = 0; row < realRatingMatrix.getRowCount(); row++)
for (int col = 0; col < realRatingMatrix.getColumnCount(); col++)
if (realRatingMatrix.getAsDouble(row, col) != 0)
summation = summation + errorOfDotProduct(realRatingMatrix.getAsDouble(row, col), predictedRatingMatrix.getAsDouble(row, col));
rmse = Math.sqrt(summation);
return rmse;
private static Matrix csvToMatrixLarge (File csvFile)
Scanner inputStream;
Matrix realRatingMatrix = SparseMatrix.Factory.zeros(610, 17000);
// Matrix realRatingMatrix = SparseMatrix.Factory.zeros(6, 7);
try
inputStream = new Scanner(csvFile);
while (inputStream.hasNext())
String ln = inputStream.next();
String[] values = ln.split(",");
double rating = Double.parseDouble(values[2]);
int row = Integer.parseInt(values[0])-1;
int col = Integer.parseInt(values[1])-1;
if (col < 1000)
realRatingMatrix.setAsDouble(rating, row, col);
inputStream.close();
catch (FileNotFoundException e)
e.printStackTrace();
return realRatingMatrix;
private static Matrix createFactorLarge (long rows, long features)
Matrix factor = DenseMatrix.Factory.zeros(rows, features);
return factor;
private static void fillInMatrixLarge (Matrix matrix)
for (int row = 0; row < matrix.getRowCount() ; row++)
for (int col = 0; col < matrix.getColumnCount(); col++)
double random = ThreadLocalRandom.current().nextDouble(5.1);
matrix.setAsDouble( (Math.floor (random * 10 ) / 10), row, col);
// return matrix;
private static Matrix getRow (Matrix matrix, int rowOfIntresst)
Matrix row = Matrix.Factory.zeros(1, matrix.getColumnCount());
for (int col = 0; col < matrix.getColumnCount(); col++)
row.setAsDouble(matrix.getAsDouble(rowOfIntresst, col), 0, col);
return row;
private static Matrix getColumn (Matrix matrix, int colOfInteresst)
Matrix column = Matrix.Factory.zeros(1, matrix.getRowCount());
for (int index = 0; index < matrix.getRowCount(); index++)
column.setAsDouble(matrix.getAsDouble(index, colOfInteresst), 0, index); //column[row] = matrix[row][colOfInteresst];
return column;
由于我没有在算法中除以零,导致错误的原因是什么?我该如何解决?
附:我正在使用通用矩阵库包
【问题讨论】:
当您调试代码时,NaN
值是在什么时候出现的?你确实调试了代码,对吧?
很难在我的手机上看到你到底想做什么......但只是想一想:矩阵的任何行或列都为零吗?
How to create a Minimal, Reproducible Example --- 可重现包括提供重现问题所需的数据。
“是什么导致了错误,因为我没有在算法中除以零?” 使用double
数学,除以零不会导致NaN
值,它会导致Infinity
或-Infinity
,除了0d / 0d
导致ArithmeticException: / by zero
和NaN / 0
导致NaN
。如您所见,除法永远不会导致 NaN
,因此请在别处寻找NaN
的来源。
代码中NaN
的潜在原因:Math.pow()
和 Math.sqrt()
,如果您阅读方法的 javadoc,您可以自己发现。
【参考方案1】:
在矩阵分解中避免 Not a Number - NaN - 错误的关键是选择正确的学习率。重要的是要注意正确的学习率总是与迭代次数有关。下面是一个说明问题的例子:
No. Of Iterations: 3
Learning Rate: 0.02
Regularization Rate: 0.02
我们在优化前的迭代 1 中以以下因素为例:
预测评分,第 4 行第 2 列:( 4.96 * 1.26 ) + ( 4.9 * 2.25 ) = 17.27
优化后的因素我们会得到:
第 4 行和第 2 列得到优化,直到我们在迭代 2 中返回它们:
预测评分,第 4 行第 2 列:( -2.31 * 233089.24 ) + ( -1.67 * -888.59 ) = -536952.2
第 4 行和第 2 列中的每个单元格都得到优化。我将展示第 1 行第 1 列的优化步骤:
-2.31 + 0.02 [ ( 2 ( 4 + 536952.2 ) ( 233089.24 ) ) - ( 0.02 * -2.31 ) ] =
-2.31 + 0.02 [ ( 2 * 536956.2 * 233089.24 ) - ( 0.02 * -2.31 ) ] =
-2.31 + 0.02 [ ( 250317425142.57 ) - ( 0.04 ) ] =
正如我们所见,在这一步我们得到了一个非常大的导数。这里的关键是选择正确的学习率。学习率决定了接近最小值的速率。如果我们让它太大,我们可能会跳过它并发散到无穷大而错过最小值。
-2.31 + 0.02 [ 250317425142,53 ] =
-2.31 + 5006348502,85 =
5006348500,54
随着优化的继续,我们将在下一次迭代中获得此单元格的 Infinity,这会在添加数字时导致 NaN 错误。
通过选择较小的学习率,我们将避免错误并迅速达到最低点。
【讨论】:
以上是关于NaN 通过矩阵分解的主要内容,如果未能解决你的问题,请参考以下文章