This examples demonstrates how to modify E-Commerce Recommendation template to further adjust score.
By default, items have a weight of 1.0. Giving an item a weight greater than 1.0 will make them appear more often and can be useful for i.e. promoted products. An item can also be given a weight smaller than 1.0 (but bigger than 0), in which case it will be recommended less often than originally. Weight values smaller than 0.0 are invalid.
You can find the complete modified source code here.
Modification
ECommAlgorithm.scala
Add a case class to represent each group items which are given the same weight.
1 2 3 4 5 | // ADDED case class WeightGroup( items: Set[String], weight: Double ) |
In ECommAlgorithm, add weightedItems
function to extract the sequence of WeightGroup
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // ADDED /** Get the latest constraint weightedItems */ def weightedItems: Seq[WeightGroup] = { try { val constr = LEventStore.findByEntity( appName = ap.appName, entityType = "constraint", entityId = "weightedItems", eventNames = Some(Seq("$set")), limit = Some(1), latest = true, timeout = Duration(200, "millis") ) if (constr.hasNext) { constr.next.properties.get[Seq[WeightGroup]]("weights") } else { Nil } } catch { case e: scala.concurrent.TimeoutException => logger.error(s"Timeout when read set weightedItems event." + s" Empty list is used. ${e}") Nil case e: Exception => logger.error(s"Error when read set weightedItems event: ${e}") throw e } } |
Modify the predictKnownUser()
, predictDefault()
and predictSimilar()
:
- add the
weights: Map[Int, Double]
parameter - adjust score according to item weights
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def predictKnownUser( userFeature: Array[Double], productModels: Map[Int, ProductModel], query: Query, whiteList: Option[Set[Int]], blackList: Set[Int], weights: Map[Int, Double] // ADDED ): Array[(Int, Double)] = { ... .map { case (i, pm) => // NOTE: features must be defined, so can call .get val s = dotProduct(userFeature, pm.features.get) // may customize here to further adjust score // ADDED val adjustedScore = s * weights(i) (i, adjustedScore) } ... } |
Lastly, modify the predict()
method. The sequence of WeightGroup
transforms into a Map[Int, Double]
that we can easily query to extract the weight given to an item, using its Int
index.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | def predict(model: ECommModel, query: Query): PredictedResult = { ... // ADDED val weights: Map[Int, Double] = (for { group <- weightedItems item <- group.items index <- model.itemStringIntMap.get(item) } yield (index, group.weight)) .toMap .withDefaultValue(1.0) ... val topScores: Array[(Int, Double)] = if (userFeature.isDefined) { // the user has feature vector predictKnownUser( userFeature = userFeature.get, productModels = productModels, query = query, whiteList = whiteList, blackList = finalBlackList, weights = weights // ADDED ) } else { ... if (recentFeatures.isEmpty) { logger.info(s"No features vector for recent items ${recentItems}.") predictDefault( productModels = productModels, query = query, whiteList = whiteList, blackList = finalBlackList, weights = weights // ADDED ) } else { predictSimilar( recentFeatures = recentFeatures, productModels = productModels, query = query, whiteList = whiteList, blackList = finalBlackList, weights = weights // ADDED ) } } ... } |
Now, to send an event to Event Server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $ curl -i -X POST http://localhost:7070/events.json?accessKey=$ACCESS_KEY \ -H "Content-Type: application/json" \ -d '{ "event" : "$set", "entityType" : "constraint", "entityId" : "weightedItems", "properties" : { "weights": [ { "items": ["i4", "i14"], "weight": 1.2 }, { "items": ["i11"], "weight": 1.5 } ] }, "eventTime" : "2014-11-02T09:39:45.618-08:00" }' |
That's it! Now your engine can predict with adjusted scores.