2013-03-27 2 views
3

Netflix가 이전에 보았고보고 즐겼던 영화를 기반으로 한 특정 영화를 어떻게 추천하는지 보았습니까? 나는 똑같은 일을하려하지만 일련의 책들을 위해 노력하고있다.이 방법으로 여러 책의 친밀도를 계산하려면 어떻게해야합니까?

53 권의 도서와 32 명의 사용자가 있습니다. 32 명의 사용자가 5 권에서 5 권 사이의 책을 평가했으며 5 권은 내가 좋아하는 책이었습니다. 서로가에 다음과 같이 사용되는 공식은 두 권의 책을 비교하는 방법 "유사한"계산하기 :

Similarity function

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; 

      } 

     } 

첫 번째 책과 나머지 책을 비교할 수있는 코드를 작성할 수 있습니다. 제 문제는 각 책이 가장 유사한 책을 찾아야한다는 것입니다. 이는 수식을 여러 번 계산한다는 의미입니다. 모든 책에서이 작업을 수행하는 방법을 모르겠습니다.

답변

3

"book vectors"의 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)); 
} 
+0

예. @dckrooney. 이 말이 맞습니다. 코사인 유사성. 나는 for 루프에 어려움을 겪고있다. 나는 거의 이것을 가지고있다. 그러나 for 루프를 적절하게 가져와 아마도 다른 가변 배열을 소개 할 필요가 있지만, 내 머리 속의 구현은 headaching이다. –

+0

해결할 수 있도록 코드의 변경 사항을 제안 할 수 있습니까? 나는 somehwhere와 다른 지그재그 배열을위한 루프가 필요하다고 생각한다. –

+0

이 코드를 작성해 주셔서 감사합니다. 나는 당신의 코드를보기 전에 작동 시켰고, 내가 한 것은 지그재그 배열을 가진 루프를 3 개 사용했다는 것이다. 귀하의 솔루션은 아마도 조직 된 고장 때문에 더 이해할 수 있습니다. –

0

이 오류입니까? 그렇지 않은 경우

denominatorYSum += compValuehold[j][i + 1] * compValuehold[j][i + 1]; 
.. 
... 
.. 
denominatorYSum = Math.Sqrt(denominatorYSum); 

은, 그냥 기본적으로 루프의, SQRT는 매우 비싼

denominatorYSum += compValuehold[j][i + 1]; 

에 코드를 변경합니다.

위의 내용이 오류라고 가정하면 두 가지 Sqrt 계산을 완전히 제거 할 수 있습니다. 또는,이 라인에

Score = eachUserProduct/Math.Sqrt(denominatorXSum * denominatorYSum); 

Math.Sqrt을 이동 (25) * Math.Sqrt (25) (25) Math.Sqrt가 높은 제곱근이 더 높은 값 또한 25 (25 * 25)입니다 . 따라서 Math.Sqrt() 호출을 완전히 제거 할 수 있으며 계산은 여전히 ​​거리 순서 (유사성)와 동일합니다.

이것은 프로그래밍 문제보다 더 심각한 문제입니다. . . 나는 네가 숙제를하지 않았 으면 좋겠어.

+0

안녕하세요 @justinDanielson, 줄은 오류가 아닙니다. i + 1은 우리가 비교할 다음 책을 나타냅니다. y는 다음 책이라고 가정합니다. 또한, 주어진 공식에서 모든 x^2를 더하고 y^2를 모두 분모와 제곱근에 합산하여 각 X 합과 Y 합을 구한 다음 곱합니다. 그것은 공식이 어떻게 생겼는지입니다. 수식의 분모를 봅니까? 내 코드 denominatorXSum은 분모의 X^2 항을 더하고 decominatorYSum은 모든 Y^2 항을 더한 다음 for 루프 바깥으로 제곱근을 제곱하고 THEN이이를 곱합니다. 내 코드는 책 1을 다른 책과 비교할 수 있습니다. –

+0

내가하려고하는 것은 모든 책에 대한 것이고, 내 코드가 첫 번째 책에 대해하는 것과 똑같은 일을한다. 위의 코드는 배열의 첫 번째 책과 가장 유사한 책을 찾는다. 모든 책에 대해 어쨌든 모든 책이 ... 유사성과 관련하여 모든 책의 비슷한 내용을 찾아내는 것입니다. –

+0

두 번째 생각에 나는 포기했다. 나는 5 시간 동안 그걸 해왔다. 그리고 논리는 나에게 두통을 주었다. 그래서 이것을 망쳤다. –

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에서 직접 선형 대수를 사용하여 계산을 수행 할 수 있으므로 성능이 향상됩니다.

그렇다면 모든 열에 대해 계산을 반복해야합니다. 큰 매트릭스가있는 경우 특히 그렇습니다. 이 문제를 해결하는 한 가지 방법은 데이터 세트의 크기를 줄이는 데 도움이되는 단일 값 분해를 사용하는 것입니다.

관련 문제