https://github.com/wzhe06/SparrowRecSys
文章目录
根据接口进行调试
RecommendationService
在主函数中,RecommendationService
是和getrecommendation
接口绑定的
context.addServlet(new ServletHolder(new RecommendationService()), "/getrecommendation");
在com.sparrowrecsys.online.service.RecommendationService#doGet
打一个断点
找到webroot/index.html
, 最后的js
看js代码:webroot/js/recsys.js
先找genre为Action
的电影,size=8
知道了调用源头在哪之后,看Java里面调用了些什么。
com.sparrowrecsys.online.datamanager.DataManager#getMoviesByGenre
从倒排索引中,根据体裁genre找出电影的ID
List<Movie> movies = new ArrayList<>(this.genreReverseIndexMap.get(genre));
MovieService
任意点击一部电影,进入这个断点:
com.sparrowrecsys.online.service.MovieService#doGet
在主函数中,MovieService
是和getmovie
接口绑定的
context.addServlet(new ServletHolder(new MovieService()), "/getmovie");
前端的请求来源我估计是webroot/js/recsys.js:182
的addMovieDetails
函数
SimilarMovieService
在主函数中,SimilarMovieService
是和getsimilarmovie
接口绑定的
context.addServlet(new ServletHolder(new MovieService()), "/getmovie");
调用的是online.recprocess.SimilarMovieProcess
类的getRecList
的静态方法
具体过程其实分为召回和排序,体现在两个方法中
List<Movie> candidates = candidateGenerator(movie);
List<Movie> rankedList = ranker(movie, candidates, model);
原生的candidateGenerator
就是根据体裁做了个单路召回,排序用的是Embedding
相似度。emb
是Movie
类的一个属性,是加载到内存中的!!!!
RecForYouService
上一个的参数是电影ID,这个的参数是用户ID,是要协同过滤?
从业务上,可以理解为以这个ID登录的用户在主页上看到的信息流?
online.recprocess.RecForYouProcess#getRecList
物料的emb是用graph Embedding算的,用户的emb是用他喜好的物料取平均算的
正常操作应该从redis里面拿特征,而不是一把梭全放内存里面
candidates
是800个根据评分(rating)得到的电影,排名第一的是4.3分的辛德勒名单
online.recprocess.RecForYouProcess#ranker
本质上就是个双塔召回
虽然看起来这个代码写得就那么回事,但仔细想想,协同过滤 → \rightarrow →矩阵分解 → \rightarrow →emb,好像也有点道理
Spark离线计算的Scala代码
Scala的语法糖实在太甜了,我已经晕了,受不了
Embedding
processItemSequence
单机伪分布式
val conf = new SparkConf()
.setMaster("local")
.setAppName("ctrModel")
.set("spark.submit.deployMode", "client")
处理出item2vec
所需的样本序列:
val samples = processItemSequence(spark, rawSampleDataPath)
ratingSamples
是评分与时间戳数据
定义排序UDF
val sortUdf: UserDefinedFunction = udf((rows: Seq[Row]) => {
rows.map {
case Row(movieId: String, timestamp: String) => (movieId, timestamp) }
.sortBy {
case (_, timestamp) => timestamp }
.map {
case (movieId, _) => movieId }
})
感觉搞不定了。。需要交互式编程才能理清楚
scala> ratingSamples
res6: org.apache.spark.sql.DataFrame = [userId: string, movieId: string ... 2 more fields]
scala> ratingSamples.where(col("rating") >= 3.5).groupBy("userId")
res7: org.apache.spark.sql.RelationalGroupedDataset = RelationalGroupedDataset: [grouping expressions: [userId: string], value: [userId: string, movieId: string ... 2 more fields], type: GroupBy]
scala> var tmp = ratingSamples.where(col("rating") <