From 26cc6575527f7417f9e7cc17bf971edfc09f6311 Mon Sep 17 00:00:00 2001 From: Daniel Ranchal Parrado Date: Sun, 29 Dec 2024 18:51:27 +0100 Subject: [PATCH] Try new rule algorithm --- .gitignore | 2 + .vscode/launch.json | 15 ++ .vscode/settings.json | 5 + build.sbt | 19 ++- lib/sparkml-som_2.12-0.2.1.jar | Bin 64766 -> 64233 bytes project/build.properties | 2 +- project/metals.sbt | 8 + project/plugins.sbt | 3 +- project/project/metals.sbt | 8 + project/project/project/metals.sbt | 8 + src/main/scala/Main.scala | 103 +++++++++++- .../SequentialTopKRecommender.scala | 148 +++++++++++++----- 12 files changed, 266 insertions(+), 55 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 project/metals.sbt create mode 100644 project/project/metals.sbt create mode 100644 project/project/project/metals.sbt diff --git a/.gitignore b/.gitignore index 9bb865f..4a1fb12 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ project/plugins/project/ .cache .bsp/ .idea +.metals +.bloop diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..678b346 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Debug (Attach to spark)", + "request": "attach", + "hostName": "localhost", + "port": 5050 + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..32cfc61 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.watcherExclude": { + "**/target": true + } +} \ No newline at end of file diff --git a/build.sbt b/build.sbt index 888de22..bddb0f4 100644 --- a/build.sbt +++ b/build.sbt @@ -1,16 +1,21 @@ version := "1.0" -scalaVersion := "2.12.15" +scalaVersion := "2.12.20" organization := "com.recsys" name := "recsys-spark" unmanagedBase := baseDirectory.value / "lib" -val sparkVersion = "3.2.1" +val sparkVersion = "3.5.4" libraryDependencies ++= Seq( - "org.scala-lang" % "scala-library" % scalaVersion.value, - "org.apache.spark" %% "spark-sql" % sparkVersion % "provided", - "org.apache.spark" %% "spark-mllib" % sparkVersion % "provided", - "org.apache.spark" %% "spark-core" % sparkVersion % "provided", - "com.github.nscala-time" %% "nscala-time" % "2.32.0" + "org.apache.spark" %% "spark-sql" % sparkVersion, + "org.apache.spark" %% "spark-mllib" % sparkVersion, + "org.apache.spark" %% "spark-core" % sparkVersion, + "com.github.nscala-time" %% "nscala-time" % "2.32.0", + "com.github.fommil.netlib" % "all" % "1.1.2" pomOnly() ) + +assemblyMergeStrategy in assembly := { + case PathList("META-INF", xs @ _*) => MergeStrategy.discard + case x => MergeStrategy.first +} diff --git a/lib/sparkml-som_2.12-0.2.1.jar b/lib/sparkml-som_2.12-0.2.1.jar index fb952dbfdecfbe83e3d78f83ea2a7144b42f6929..91f20d43e3f5946eff1931b405f2b201ef383f2b 100644 GIT binary patch delta 43588 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 H=jHzc!>O_v delta 44056 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 nuK>>iU<*v2xCtW)=!i-(Ne=$j5oip< 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 diff --git a/project/build.properties b/project/build.properties index c8fcab5..73df629 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.6.2 +sbt.version=1.10.7 diff --git a/project/metals.sbt b/project/metals.sbt new file mode 100644 index 0000000..e069ffa --- /dev/null +++ b/project/metals.sbt @@ -0,0 +1,8 @@ +// format: off +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.6") + +// format: on diff --git a/project/plugins.sbt b/project/plugins.sbt index e7f7e88..db41917 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1,2 @@ -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") \ No newline at end of file +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.0") \ No newline at end of file diff --git a/project/project/metals.sbt b/project/project/metals.sbt new file mode 100644 index 0000000..e069ffa --- /dev/null +++ b/project/project/metals.sbt @@ -0,0 +1,8 @@ +// format: off +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.6") + +// format: on diff --git a/project/project/project/metals.sbt b/project/project/project/metals.sbt new file mode 100644 index 0000000..e069ffa --- /dev/null +++ b/project/project/project/metals.sbt @@ -0,0 +1,8 @@ +// format: off +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.6") + +// format: on diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 3015bc9..f4ecf30 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -261,6 +261,85 @@ object Main { }) } + def sequentialTopKCrossValidation(): Seq[(Double, Double, Double)] = { + val spark = SparkSession.getActiveSession.orNull + + val recSys = new SequentialTopKRecommender(5, 1682).setGridSize( + 3, 3 + ).setPeriods(5).setMinParamsApriori( + 0.005, 0.80 + ).setMinParamsSequential( + 0.001, 0.60 + ) + + val predictions_accumulator1 = new ListBufferAccumulator[(Double, Double, Double)] + spark.sparkContext.register(predictions_accumulator1, "predictions1") + val predictions_accumulator2 = new ListBufferAccumulator[(Double, Double, Double)] + spark.sparkContext.register(predictions_accumulator2, "predictions2") + val predictions_accumulator3 = new ListBufferAccumulator[(Double, Double, Double)] + spark.sparkContext.register(predictions_accumulator3, "predictions3") + val predictions_accumulator4 = new ListBufferAccumulator[(Double, Double, Double)] + spark.sparkContext.register(predictions_accumulator4, "predictions4") + val predictions_accumulator5 = new ListBufferAccumulator[(Double, Double, Double)] + spark.sparkContext.register(predictions_accumulator5, "predictions5") + + Seq(1, 2, 3, 4, 5).map(index => { + println("Fold " + index) + val train = dataset("data/train-fold" + index + ".csv") + val test = dataset("data/test-fold" + index + ".csv") + + val accumulator = index match { + case 1 => predictions_accumulator1 + case 2 => predictions_accumulator2 + case 3 => predictions_accumulator3 + case 4 => predictions_accumulator4 + case 5 => predictions_accumulator5 + } + + println("train") + recSys.fit(train) + println("termina train") + + println("datos test empiezan") + val testData = test.groupBy("user_id").agg( + collect_list(col("item_id")).as("items"), + collect_list(col("rating")).as("ratings") + ).collect() + println("datos test terminan") + + testData.foreach(row => { + val userId = row.getInt(0) + val items = row.getList(1).toArray() + val ratings = row.getList(2).toArray() + + val relevant = items.zip(ratings).filter( + _._2.asInstanceOf[Double] >= 4.0 + ).map(_._1.asInstanceOf[Int]).toSet + + val selected = recSys.transform( + train.filter(col("user_id") === userId) + ) + + accumulator.add( + new RankingMetrics(k = 5, selected.map(_._1).toSet, relevant).getRankingMetrics, + ) + }: Unit) + + val metricPerUser = accumulator.value + val sumMetrics = metricPerUser.reduce((a, b) => { + (a._1 + b._1, a._2 + b._2, a._3 + b._3) + }) + + val finalMetrics = ( + sumMetrics._1 / metricPerUser.length, + sumMetrics._2 / metricPerUser.length, + sumMetrics._3 / metricPerUser.length + ) + + finalMetrics + }) + } + def hybridCrossValidation(firstRecSys: BaseRecommender, secondRecSys: BaseRecommender, weightFirstRecSys: Double, weightSecondRecSys: Double, topK: Int, numberOfItems: Int): Seq[(Double, Double, Double)] = { val spark = SparkSession.getActiveSession.orNull @@ -337,7 +416,20 @@ object Main { def main(args: Array[String]): Unit = { val spark = SparkSession.builder().master( "local[*]" - ).config( + ) + .config( + "spark.driver.memory", "6gb" + ) + .config( + "spark.executor.memory", "6gb" + ) + .config( + "spark.driver.extraJavaOptions", "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED" + ) + .config( + "spark.executor.extraJavaOptions", "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED -agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=n" + ) + .config( "spark.sql.autoBroadcastJoinThreshold", "-1" ).config( "spark.jars", "lib/sparkml-som_2.12-0.2.1.jar" @@ -347,9 +439,9 @@ object Main { spark.sparkContext.setLogLevel("ERROR") println("Try here your recommender") -// User based approach -// val results = userBasedTopKCrossValidation(new PearsonSimilarity, 25, 5, 1682) -// println(results) + // User based approach + // val results = userBasedTopKCrossValidation(new PearsonSimilarity, 25, 5, 1682) + // println(results) // Item based approach // val results = itemBasedTopKCrossValidation(new CosineSimilarity, 25, 5, 1682) @@ -379,6 +471,9 @@ object Main { // val results = hybridCrossValidation(recSys1, recSys2, 0.6, 0.4, 5, 1682) // println(result) + val results = sequentialTopKCrossValidation() + println(results) + spark.stop() } } diff --git a/src/main/scala/recommender/sequential/SequentialTopKRecommender.scala b/src/main/scala/recommender/sequential/SequentialTopKRecommender.scala index 671a5f3..b090599 100644 --- a/src/main/scala/recommender/sequential/SequentialTopKRecommender.scala +++ b/src/main/scala/recommender/sequential/SequentialTopKRecommender.scala @@ -18,14 +18,17 @@ package recommender.sequential import org.apache.spark.ml.fpm.FPGrowth +import scala.jdk.CollectionConverters._ import org.apache.spark.ml.linalg.Vectors -import org.apache.spark.sql.functions.{col, collect_list, collect_set, datediff, max, min, monotonically_increasing_id, struct, udf, when, window} +import org.apache.spark.sql.expressions.Window +import org.apache.spark.sql.functions.{row_number, coalesce, typedLit, rank, col, collect_list, collect_set, datediff, max, min, monotonically_increasing_id, struct, udf, when, window, desc} import org.apache.spark.sql.DataFrame import som.{SOM, SOMModel} import com.github.nscala_time.time.Imports._ import recommender.BaseRecommender +import scala.collection.mutable.WrappedArray class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) extends BaseRecommender(numberOfItems = numberOfItems) { @@ -38,7 +41,7 @@ class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) ext private var _periods: Seq[(Long, String, String)] = null private var _periodsIds: List[Long] = null private var _numberPeriods: Int = -1 - private var _rules: Array[(Array[String], Array[String], Double, Double)] = null + private var _rules: Array[(Array[Int], Array[Int], Double, Double)] = null private var _heightGridSom: Int = 5 private var _widthGridSom: Int = 5 private var _minSupportApriori: Double = 0.0 @@ -187,9 +190,7 @@ class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) ext val generated = row.map(element => { ( element.head._1, - element.head._2.map( - _.toString + "_" + (element.head._1 - this._periodsIds.length + 1).toString - ) + element.head._2 ) }) @@ -234,7 +235,7 @@ class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) ext } else { rulesWithScore.sortWith( _._5 > _._5 - ).head._2.head.split("_").head.toInt + ).head._2.head } } @@ -416,52 +417,94 @@ class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) ext } private def obtainRules(): Unit = { + val to_items = udf((row: List[List[Int]]) => { + row.flatten.toList + }) + // udf to flatten list and add metadata to each item - val flatList = udf((row: List[List[(Long, List[Int])]]) => { + val to_sequences = udf((row: List[List[(Long, List[String])]]) => { // getting list of sets being the first element the id of the period // and as second element the item with its period val generated = row.map(element => { ( element.head._1, - element.head._2.map( - _.toString + "_" + (element.head._1 - this._periodsIds.length + 1).toString - ) + element.head._2.map(_.toInt).toSeq ) }) // ordering list of sequence (returning [] if that period has not items) // and returning a flat map (representing a transaction for the rule extractor) - this._periodsIds.map((id: Long) => { + val item_sequences = this._periodsIds.map((id: Long) => { if(generated.exists(_._1 == id)) { generated.find(_._1 == id).orNull } else { (id, Seq()) } - }).sortWith(_._1 < _._1).flatMap(_._2) + }).sortWith(_._1 < _._1).filter(!_._2.isEmpty).map(_._2) + + var seen = Set[Int]() + + // Process from newest to oldest, filtering out previously seen items. + item_sequences.reverse.map { seq => + val filtered = seq.filterNot(seen.contains) + // Mark any kept items as seen + seen = seen ++ filtered + filtered + }.reverse.filter(_.nonEmpty) + }) // udf to filter rules which consequent does not have an item from the period 0 (actual period) - val filterAntecedent = udf((row: Array[String]) => { - row.filter(!_.endsWith("_0")) + val filterAntecedent = udf((row: Array[Int]) => { + row }) // udf to get X union Y from X -> Y - val getXY = udf((column1: List[String], column2: List[String]) => { + val getXY = udf((column1: List[Int], column2: List[Int]) => { column1 ++ column2 }) - // get transactions per period for each user - val transactions = this._transactionDf.groupBy("user_id", "period_id").agg( - collect_set(col("transaction_cluster")).as("transaction_clusters") + val allClusters = this._transactionDf + .groupBy("user_id", "period_id") + .agg( + collect_set(col("transaction_cluster")).as("transaction_clusters") + ) + + // 1. Compute frequency of each (user_id, period_id, transaction_cluster) + val clusterCounts = this._transactionDf + .groupBy("user_id", "period_id", "transaction_cluster") + .count() + + // 2. Rank the clusters using a window specification, then keep only top 2 + val windowSpec = Window.partitionBy("user_id", "period_id").orderBy(desc("count")) + + // Take top-2 clusters + val topTwoClusters = clusterCounts + .withColumn("rn", row_number().over(windowSpec)) + .filter(col("rn") <= 3) + .groupBy("user_id", "period_id") + .agg(collect_set("transaction_cluster").as("top_2_clusters")) + + val transactions = allClusters + .join(topTwoClusters, Seq("user_id","period_id"), "left") // preserve rows even if there's no "top_2_clusters" + .select( + col("user_id"), + col("period_id"), + col("transaction_clusters"), + coalesce(col("top_2_clusters"), typedLit(Seq.empty[String])).as("top_2_clusters") ).groupBy("user_id", "period_id").agg( - collect_list(struct(col("period_id"), col("transaction_clusters"))).as("tuple_period_cluster") + collect_list(struct(col("period_id"), col("top_2_clusters"))).as("tuple_period_cluster") ).groupBy("user_id").agg( collect_list(col("tuple_period_cluster")).as("tuple_period_cluster_per_user") - ).withColumn( - "items", - flatList( + ) + .withColumn( + "sequences", + to_sequences( col("tuple_period_cluster_per_user") ) + ).withColumn( + "items", + to_items(col("sequences")) ).drop("tuple_period_cluster_per_user").orderBy("user_id") // train fpgrowth model @@ -478,29 +521,44 @@ class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) ext // and removing columns of metrics related to rules // because they were extracted from the original rules // After that, distinct rules are obtained - val rules = model.associationRules.filter(row => { - val consequent = row.getList(1).toArray() - consequent.head.asInstanceOf[String].endsWith("_0") - }).withColumn( - "antecedent", - filterAntecedent(col("antecedent")) - ).filter(row => row.getList(0).toArray().nonEmpty).drop( + model.associationRules.show(truncate = false) + val rules = model.associationRules.filter(row => row.getList(0).toArray().nonEmpty).drop( "confidence", "lift", "support" ).distinct() // Solving support and confidence for the new set of rules val numberOfTransactions = transactions.count() - val transactionsArray = transactions.select("items").collect().map(_.getList(0).toArray()) + val transactionsArray = transactions.select("sequences").collect().map( + _.getList(0).asScala.toList + ).toList // udf for support - val getSupport = udf((row: List[String]) => { - transactionsArray.map(transaction => { - if (row.toSet.subsetOf(transaction.map(_.asInstanceOf[String]).toSet)) { - 1.0 - } else { - 0.0 + val getSupport = udf((pattern: Array[Int]) => { + def containsSubsequence(seq: List[WrappedArray[Int]], pattern: List[Int]): Boolean = { + var pIndex = 0 + var sIndex = 0 + + // while we still have itemsets in seq AND items in pattern + while (sIndex < seq.size && pIndex < pattern.size) { + val currentItemset = seq(sIndex) + val nextPatternItem = pattern(pIndex) + + // if the current itemset contains that pattern item + if (currentItemset.contains(nextPatternItem)) { + // move to the next item in the pattern + pIndex += 1 + } + // always move to the next itemset + sIndex += 1 } - }).sum / numberOfTransactions.toDouble + + // if we've matched the whole pattern (pIndex == pattern.size), success + pIndex == pattern.size + } + + transactionsArray.count( + meow => containsSubsequence(meow, pattern.toList) + ).toDouble / numberOfTransactions.toDouble }) // rules with support and confidence @@ -516,17 +574,23 @@ class SequentialTopKRecommender(kRecommendedItems: Int, numberOfItems: Long) ext ).withColumn( "confidence", col("support") / col("support_antecedent") - ).drop("XY", "support_antecedent") + ).drop("XY", "support_antecedent").orderBy(desc("confidence")) + + sequentialRules.show(truncate = false, numRows = 10) // filter sequential rules with min support and confidence - this._rules = sequentialRules.filter( + val gatito = sequentialRules.filter( col("support") > this._minSupportSequential ).filter( col("confidence") > this._minConfidenceSequential - ).collect().map(rule => { + ) + + gatito.show(truncate = false, numRows = 1000) + + this._rules = gatito.collect().map(rule => { ( - rule.getList(0).toArray().map(_.asInstanceOf[String]), - rule.getList(1).toArray().map(_.asInstanceOf[String]), + rule.getList(0).toArray().map(_.asInstanceOf[Int]), + rule.getList(1).toArray().map(_.asInstanceOf[Int]), rule.getDouble(2), rule.getDouble(3) )