본문 바로가기

데이터 분석

Spark - Ranking systems (2) - 예제 테스트

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


앞장에서 관련 내용을 알아봤으니 이제 실제 테스트를 진행해 보자.


계산은 컴퓨터가 알아서 해주니깐 할 필요가 없지만 


대충 어떻게 나오는지는 꼭 앞의 내용을 쓱~~ 보자.







 Sample 


▶ Spark Documents의 예제와 샘플 사용


▶ 제공되는 고객정보, 구매물품번호, 평가점수의 샘플 데이터 사용



USER : PRODUCT : RATING


0::2::3 -> 0번 유저가 2번 물건을 샀는데 평점이 3점이다.

0::3::1

0::5::2

0::9::4

0::11::1

..

..

1::2::2

1::3::1

1::4::2

1::6::1

..
..
29::97::1
29::99::1

 






 상위 평점 데이터 추출 및 변환


▶ 샘플 데이터에서 3점 이상인 것만 유저별, 물품 정보 추출


▶ 예제에서 평점 5점이나 4점은 또이또이로 보는거 같다.



                위 형식의 샘플 데이터를 읽고

                String path = "/home/ksu/spark-2.1.0-bin-hadoop2.6/data/mllib/sample_movielens_data.txt";

JavaRDD<String> data = jsc.textFile(path);




                평점을 Max 2.5로 변환하고


JavaRDD<Rating> ratings = data.map(


  new Function<String, Rating>() {


    public Rating call(String line) {

      String[] parts = line.split("::");

        return new Rating(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Double

          .parseDouble(parts[2]) - 2.5);

    }

  }

);

ratings.cache();




                Max 2.5로 변환한 샘플을  양수면 1로 음수면 0으로 변환  (상위 점수 1, 하위 0)

JavaRDD<Rating> binarizedRatings = ratings.map(

  new Function<Rating, Rating>() {

    public Rating call(Rating r) {

      double binaryRating;

      if (r.rating() > 0.0) {

        binaryRating = 1.0;

      } else {

        binaryRating = 0.0;

      }

      return new Rating(r.user(), r.product(), binaryRating);

    }

  }

);




// 유저로 그룹핑

JavaPairRDD<Object, Iterable<Rating>> userMovies = binarizedRatings.groupBy(

  new Function<Rating, Object>() {

    public Object call(Rating r) {

      return r.user();

    }

  }

);



// 1인 상위 평점만 추출

JavaPairRDD<Object, List<Integer>> userMoviesList = userMovies.mapValues(

  new Function<Iterable<Rating>, List<Integer>>() {

    public List<Integer> call(Iterable<Rating> docs) {

      List<Integer> products = new ArrayList<Integer>();

      for (Rating r : docs) {

        if (r.rating() > 0.0) {

          products.add(r.product());

        }

      }

      return products;

    }

  }

);






 추천



                모델 생성

                final MatrixFactorizationModel model = ALS.train(JavaRDD.toRDD(ratings), 10, 10, 0.01);


                [여기서 참고내용]

MatrixFactorizationModel train(RDD<Rating> ratings,  int rank,  int iterations,  double lambda) 


ratings

데이터


iterations

ALS 반복횟수, 데이터가 적을 경우 반복 학습 하는 것 같음


lambda 

매개변수로 overfitting 등을 방지하기 위해 설정 값으로 0.1 / 0.01 / 0.001 설정


rank

사용할 특성 수로, 쉽게 설명 하자면 어떠한 분류의 등급이 3개의 특성으로[예로 영화 등급을 성별, 연령, 직업 3가지로] 완벽하게 예측 된다면 3으로 설정하면 되지만 데이터 분석이 완벽하지 않다면  5-10의 등급으로 설정하여 시작한 다음, 결과를 향상시키지 않을 때까지 한 번에 5를 증가시켜 실험을 통해 데이터 세트의 최상위 순위를 결정하는 방법을 추천한다.







다시 코드로 돌아와서 모델을 통해 각 유저에게 추천할 10개를 뽑고

JavaRDD<Tuple2<Object, Rating[]>> userRecs = model.recommendProductsForUsers(10).toJavaRDD();


[추천 데이터인 userRecs를 출력하면 아래와 같다.]

4 : Rating(4,53,3.3676532631739)Rating(4,74,1.9648877917617624) ...

16 : Rating(16,75,2.98930402124235)Rating(16,22,2.7903989138966274)...

28 : Rating(28,93,4.989832986718977)Rating(28,76,4.367767761551139)...

..

..

24 : Rating(24,69,2.645187738545377)Rating(24,96,2.6096702239621554)...


으로 4번 유저가 53번 물품을 3.36 평점으로 좋아한다고 추천






추천 10개의 예측평점을 0~1로 변환

5->2.5 / 4->1.5 는 관련성 차이가 비슷하다고 보고 1로 똑같이 변환하고

나머지는 0과 1사이 소수점 단위로

                JavaRDD<Tuple2<Object, Rating[]>> userRecsScaled = userRecs.map(

 new Function<Tuple2<Object, Rating[]>, Tuple2<Object, Rating[]>>() {

   public Tuple2<Object, Rating[]> call(Tuple2<Object, Rating[]> t) {

     Rating[] scaledRatings = new Rating[t._2().length];

     for (int i = 0; i < scaledRatings.length; i++) {

       double newRating = Math.max(Math.min(t._2()[i].rating(), 1.0), 0.0);

       scaledRatings[i] = new Rating(t._2()[i].user(), t._2()[i].product(), newRating);

     }

     return new Tuple2<Object, Rating[]>(t._1(), scaledRatings);

   }

 }

);


                JavaPairRDD<Object, Rating[]> userRecommended = JavaPairRDD.fromJavaRDD(userRecsScaled);


                JavaPairRDD<Object, List<Integer>> userRecommendedList = userRecommended.mapValues(

 new Function<Rating[], List<Integer>>() {

   public List<Integer> call(Rating[] docs) {

     List<Integer> products = new ArrayList<Integer>();

     for (Rating r : docs) {

       products.add(r.product());

     }

     return products;

   }

 }

);


userRecommendedList를 찍어보면 아래와 같다.


바로 위의 결과와 비교하면 0~1 사이 값으로 최종 변경됨을 확인


높은 추천 값은 1로 변환하고 0보다 큰 낮은 추천 값은 그대로 사용하여 정렬함 (높은 점수별로 정렬해야 하니깐)


4 : Rating(4,53,1.0)Rating(4,2,1.0)..  Rating(0,91,0.20539317108953062)

16 : Rating(16,85,1.0)Rating(16,90,1.0)..

..

..

24 : Rating(24,26,1.0)Rating(24,96,1.0)..






 평가


                JavaRDD<Tuple2<List<Integer>, List<Integer>>> relevantDocs = userMoviesList.join(

 userRecommendedList).values();


RankingMetrics<Integer> metrics = RankingMetrics.of(relevantDocs);



Precision and NDCG at k

Integer[] kVector = {1, 2, 3, 4, 5};

for (Integer k : kVector) {

 System.out.format("Precision at %d = %f\n", k, metrics.precisionAt(k));

 System.out.format("NDCG at %d = %f\n", k, metrics.ndcgAt(k));

}


Mean average precision

System.out.format("Mean average precision = %f\n", metrics.meanAveragePrecision());

 


Precision at 1 = 0.400000

NDCG at 1 = 0.400000

Precision at 2 = 0.433333
NDCG at 2 = 0.425790

Precision at 3 = 0.477778

NDCG at 3 = 0.458845

Precision at 4 = 0.458333

NDCG at 4 = 0.448952

Precision at 5 = 0.473333

NDCG at 5 = 0.460023


Mean average precision = 0.286827




Precision at 1 계산

userMoviesList  /  userRecommendedList  ->  Top 1번 추천 목록 포함 유무


[([29, 40, 41, 52, 60, 62, 63, 70, 87, 88],[53, 2, 62, 70, 74, 41, 29, 76, 87, 52]),  -> 1

([5, 29, 47, 51, 54, 85, 90, 94, 96, 98],[75, 52, 90, 85, 22, 51, 54, 3, 77, 29]), -> 0

..

..

..

([9, 13, 20, 23, 36, 48, 49, 50, 55, 56, 64, 68, 90],[62, 55, 46, 17, 18, 90, 68, 49, 22, 94])] -> 0



1 + 0 + ... + 0 / 총 개수(30)  = 0.40000


NDCG at K

도 직접 계산해 볼려고 했지만 너~~무 어려워서 패스~






                샘플 데이터의 유저, 물품 정보

JavaRDD<Tuple2<Object, Object>> userProducts = ratings.map(

 new Function<Rating, Tuple2<Object, Object>>() {

   public Tuple2<Object, Object> call(Rating r) {

     return new Tuple2<Object, Object>(r.user(), r.product());

   }

 }

);


                평점 예측

JavaPairRDD<Tuple2<Integer, Integer>, Object> predictions = JavaPairRDD.fromJavaRDD(

 model.predict(JavaRDD.toRDD(userProducts)).toJavaRDD().map(

   new Function<Rating, Tuple2<Tuple2<Integer, Integer>, Object>>() {

     public Tuple2<Tuple2<Integer, Integer>, Object> call(Rating r) {

       return new Tuple2<Tuple2<Integer, Integer>, Object>(

         new Tuple2<Integer, Integer>(r.user(), r.product()), r.rating());

     }

   }

 ));

                실제 평점 : 예측 평점

JavaRDD<Tuple2<Object, Object>> ratesAndPreds =

 JavaPairRDD.fromJavaRDD(ratings.map(

   new Function<Rating, Tuple2<Tuple2<Integer, Integer>, Object>>() {

     public Tuple2<Tuple2<Integer, Integer>, Object> call(Rating r) {

       return new Tuple2<Tuple2<Integer, Integer>, Object>(

         new Tuple2<Integer, Integer>(r.user(), r.product()), r.rating());

     }

   }

 )).join(predictions).values();


ratesAndPreds 출력

[(-0.5,-0.29681151453477883), 

(-1.5,-1.533072070128673), 

(-1.5,-1.2837009769873864), 

(-1.5,-1.5222169522319295), 

(-1.5,-0.7860986167518518), 

....

(-1.5,-1.3909584989232666)]






RegressionMetrics regressionMetrics = new RegressionMetrics(ratesAndPreds.rdd());


Root mean squared error

System.out.format("RMSE = %f\n", regressionMetrics.rootMeanSquaredError());

R-squared 

System.out.format("R-squared = %f\n", regressionMetrics.r2());



RMSE 정확성(Accuracy)에 대한 질적인 척도로 오차(잔차)의 제곱에 대해 평균을 취하고 이를 제곱근한 것


R-square값은 통계적 검증 지표로 변수간의 상관계수를 제곱한 것으로 원인변수가 결과변수를 설명하는 정도로 원인변수들의 분산이 결과변수의 분산을 95.7% 정도 설명한다는 뜻으로 적용한 모형으로 설명 가능한 부분의 비율

학계에서는 0.6이상, 실무에서는 0.4이상이면 의미가 있다고 해석한다.


RMSE = 0.219238

R-squared = 0.963394







 파라미터 변경


final MatrixFactorizationModel model = ALS.train(JavaRDD.toRDD(ratings), 25, 10, 0.01);


Rank를 5씩 증가해서 검증 평가가 바뀌는지 확인해봄 


Precision at 1 = 0.533333

NDCG at 1 = 0.533333

Precision at 2 = 0.616667

NDCG at 2 = 0.597809

Precision at 3 = 0.655556

NDCG at 3 = 0.629608

Precision at 4 = 0.658333

NDCG at 4 = 0.635839

Precision at 5 = 0.653333

NDCG at 5 = 0.635510


Mean average precision = 0.527762

RMSE = 0.034665

R-squared = 0.999118