앞장에서 관련 내용을 알아봤으니 이제 실제 테스트를 진행해 보자.
계산은 컴퓨터가 알아서 해주니깐 할 필요가 없지만
대충 어떻게 나오는지는 꼭 앞의 내용을 쓱~~ 보자.
□ 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 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씩 증가해서 검증 평가가 바뀌는지 확인해봄
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
|
'데이터 분석' 카테고리의 다른 글
MLlib(K-Means) + SparkStreaming 실시간 Clustering (2) (0) | 2018.02.14 |
---|---|
MLlib(K-Means) + SparkStreaming 실시간 Clustering (1) (0) | 2018.02.13 |
Spark - Ranking systems (1) - 이론 (2) | 2017.07.24 |
Spark - AssociationRules (0) | 2017.07.21 |
Spark - Multiclass classification (0) | 2017.07.20 |