你有没有见过Netflix如何根据你以前看过的电影和喜欢看的电影推荐某些电影?我试图做同样的事情,但对于一套书。
我有53本书和32位用户。 32位用户对每本书的评分从5到-5,其中5本是我喜欢的。用于计算“相似”两本书如何相互比较的公式如下:
x1*y1
代表用户对书籍x和书籍y的评分,x2*y2
代表第2位用户对相同2本书籍的评分,并继续为所有用户提供。
传递给此方法的数组是主数组。主阵列的每个元素对应于用户,并且用户阵列中的每个元素对应于书。 (32个用户数组,每个数组本身就是一个53个元素的数组)
保持每个用户评级的数组是有序的,compValuehold[0][0]
表示第一个用户对第一本书的评分,compValuehold[0][2]
表示第一个用户对第二本书的评分等等。
public static void DisplayRatings(double[][] compValuehold)
{
double eachUserProduct = 0;
double denominatorXSum = 0;
double denominatorYSum = 0;
double Score = 0;
int counterForScore = 0;
double[] calculatedValues = new double[52];
//this for loop should calculate each book's ratings and store it
//in an array
for (int i = 0; i < 52; i++)
{
for (int j = 0; j < 32; j++)
{
eachUserProduct += compValuehold[j][i] * compValuehold[j][i + 1];
denominatorXSum += compValuehold[j][i] * compValuehold[j][i];
denominatorYSum += compValuehold[j][i + 1] * compValuehold[j][i + 1];
}
denominatorXSum = Math.Sqrt(denominatorXSum);
denominatorYSum = Math.Sqrt(denominatorYSum);
Score = eachUserProduct / (denominatorXSum * denominatorYSum);
calculatedValues[counterForScore] = Score;
counterForScore += 1;
denominatorXSum = 0;
denominatorYSum = 0;
eachUserProduct = 0;
}
}
我能够编写代码来比较第一本书和其他书籍。 我的问题是我需要找出每本书哪本书最相似。这意味着很多次计算这个公式。我不知道如何为所有书籍做这个。
答案 0 :(得分:3)
您正在做的就是确定“书籍向量”的Cosine Similarity,其中每个向量由每个用户对特定书籍的评分组成。
尝试在一个函数中执行此操作可能会让您在调试时遇到麻烦; 我建议将你的问题分解成更容易管理的部分:
compValuehold
矩阵similarity(a, b) == similarity(b, a)
)这种方法还可以让你更容易改变你的相似度函数,如果你想出一个更好的比较书籍的方法。
这是前两个子问题的示例实现(请记住它们不是特别有效):
static int[] GetBookVector(int[][] ratingMatrix, int bookIndex)
{
int[] book = new int[ratingMatrix.Length];
for (int i = 0; i < ratingMatrix.Length; i++)
{
book[i] = ratingMatrix[i][bookIndex];
}
return book;
}
static double Similarity(int[] v1, int[] v2)
{
if (v1.Length != v2.Length)
{
throw new ArgumentException("Vectors must be of the same length.");
}
int numerator = 0;
double v1Norm = 0;
double v2Norm = 0;
for (int i = 0; i < v1.Length; i++)
{
numerator += v1[i] * v2[i];
v1Norm += v1[i] * v1[i];
v2Norm += v2[i] * v2[i];
}
v1Norm = Math.Sqrt(v1Norm);
v2Norm = Math.Sqrt(v2Norm);
return (numerator / (v1Norm * v2Norm));
}
答案 1 :(得分:2)
正如@dckrooney指出的那样,您正在计算两个向量之间的余弦相似度,每个向量代表所有用户的“评级配置文件”。从头开始编写该函数很好,但您可以考虑使用线性代数库来简化您的工作。例如,使用像Math.NET这样的库,您可以将数组表示为矩阵,比如说等级,然后您可以提取列并以更直接的方式执行计算:
public double Similarity(DenseMatrix matrix, int col1, int col2)
{
var column1 = matrix.Column(col1);
var column2 = matrix.Column(col2);
var similarity = column1.DotProduct(column2) / (column1.Norm(2)+column2.Norm(2));
return similarity;
}
使用原始数组可能会获得一些轻微的性能优势,但可以说,代码更易读,也更容易维护。此外,Math.NET允许您使用native providers并直接在CPU上使用线性代数运行计算,这可以为您带来不错的性能提升。
除此之外,是的,你必须为每一列重复计算,这可能变得非常昂贵,特别是如果你有一个大矩阵。解决此问题的一种方法是使用奇异值分解,它可以帮助您减小数据集的大小。
答案 2 :(得分:0)
这是一个错误吗?
denominatorYSum += compValuehold[j][i + 1] * compValuehold[j][i + 1];
..
...
..
denominatorYSum = Math.Sqrt(denominatorYSum);
如果不是,只需将代码更改为
即可denominatorYSum += compValuehold[j][i + 1];
Sqrt非常昂贵,它本质上是一个循环。
假设上面是一个错误,我将彻底摆脱两个Sqrt计算。或者将它移到这一行,
Score = eachUserProduct / Math.Sqrt(denominatorXSum * denominatorYSum);
Math.Sqrt(25)* Math.Sqrt(25)为25. Math.Sqrt(25 * 25)为25.此外,更高的值具有更高的平方根。所以你可以完全摆脱Math.Sqrt()调用,并且距离排序(相似性)的计算仍然是相同的。
这更像是数学问题,而不是编程问题。 。 。我希望我没有做你的功课。