| package org.template.recommendation |
| |
| import io.prediction.controller.PAlgorithm |
| import io.prediction.controller.Params |
| |
| import org.apache.spark.SparkContext |
| import org.apache.spark.SparkContext._ |
| import org.apache.spark.rdd.RDD |
| import org.apache.spark.mllib.recommendation.ALS |
| import org.apache.spark.mllib.recommendation.{Rating => MLlibRating} |
| import org.apache.spark.mllib.recommendation.ALSModel |
| |
| import grizzled.slf4j.Logger |
| |
| case class ALSAlgorithmParams( |
| rank: Int, |
| numIterations: Int, |
| lambda: Double) extends Params |
| |
| class ALSAlgorithm(val ap: ALSAlgorithmParams) |
| extends PAlgorithm[PreparedData, ALSModel, Query, PredictedResult] { |
| |
| @transient lazy val logger = Logger[this.type] |
| |
| def train(data: PreparedData): ALSModel = { |
| // Convert user and item String IDs to Int index for MLlib |
| val mllibRatings = data.ratings.map( r => |
| // MLlibRating requires integer index for user and item |
| MLlibRating(data.users(r.user).toInt, |
| data.items(r.item).toInt, r.rating) |
| ) |
| val m = ALS.train(mllibRatings, ap.rank, ap.numIterations, ap.lambda) |
| new ALSModel( |
| rank = m.rank, |
| userFeatures = m.userFeatures, |
| productFeatures = m.productFeatures, |
| users = data.users, |
| items = data.items) |
| } |
| |
| def predict(model: ALSModel, query: Query): PredictedResult = { |
| // Convert String ID to Int index for Mllib |
| model.users.get(query.user).map { userInt => |
| // recommendProducts() returns Array[MLlibRating], which uses item Int |
| // index. Convert it to String ID for returning PredictedResult |
| val itemScores = model.recommendProducts(userInt.toInt, query.num) |
| .map (r => ItemScore(model.items(r.product.toLong), r.rating)) |
| new PredictedResult(itemScores) |
| }.getOrElse{ |
| logger.info(s"No prediction for unknown user ${query.user}.") |
| new PredictedResult(Array.empty) |
| } |
| } |
| |
| } |