首先来看一个例子:
我们已经知道
1)、用户u1喜欢的电影是A,B,C
2)、用户u2喜欢的电影是A,C,E,F
3)、用户u3喜欢的电影B,D
我们需要解决的问题是:决定对u1是不是应该推荐F这部电影
基于内容的做法:要分析F的特征和u1所喜欢的A,B,C的特征,需要知道的信息是A(战争片),B(战争片),C(剧情片),如果F(战争片),那么F很大程度上可以推荐给u1,这是基于内容的做法,你需要对item进行特征建立和建模,利用模型给F打分
引入item属性的content Based推荐
image.png1)对item内容进行分析(一般对item描述性信息和标签等内容进行分析),对item内容进行中文分词,得到item_id、word以及score
2)、对上一步得到的item_id、word、score建立一个倒排索引即word [item_id1:score,item_id2:score,.....],以word为key,进行groupby,然后对score进行排序
3)、对历史浏览数据进行特征处理和建模
4)、然后对推荐结果进行重新打分排序
优缺点:
优点:
1、提升推荐结果的相关性
2、结果可解释
3、推荐结果容易被用户感知
缺点:
1、无个性化
2、依赖对item的深度分析
引入user属性的Content Based推荐
image.png1)对item内容进行分析(一般对item描述性信息和标签等内容进行分析),对item内容进行中文分词,得到item_id、word以及score
2)、对上一步得到的item_id、word、score建立一个倒排索引即word [item_id1:score,item_id2:score,.....],以word为key,进行groupby,然后对score进行排序
3)、用户行为数据进行分析,对用户兴趣建模
4)、然后对推荐结果进行重新打分排序
优缺点:
优点:
1、用户模型刻画了用户兴趣需求
2、推荐形式多样,具有个性化
3、结果可解释
缺点:
推荐精度低
马太效应
用户行为稀疏导致覆盖率低
代码如下:
package cb
import com.huaban.analysis.jieba.{JiebaSegmenter, SegToken}
import com.huaban.analysis.jieba.JiebaSegmenter.SegMode
import org.apache.spark.SparkConf
import org.apache.spark.ml.feature.StopWordsRemover
import org.apache.spark.sql.{DataFrame, SparkSession}
import org.apache.spark.sql.functions._
import scala.util.matching.Regex
object CBTest {
case class Music(music_id:String, music_content:String)
case class StopWords(stopword:String)
case class IdfWords(word:String,idf:String)
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.registerKryoClasses(Array(classOf[JiebaSegmenter]))
val spark = SparkSession
.builder()
.appName("cb")
.master("local")
.config("spark.sql.warehouse.dir","D:\\hadoop\\sparkTest\\data\\music_meta.txt")
.config("spark.sql.warehouse.dir","D:\\hadoop\\sparkTest\\data\\stopwords.txt")
.config("spark.sql.warehouse.dir","D:\\hadoop\\sparkTest\\data\\idf.txt")
.config(conf)
.getOrCreate()
import spark.implicits._
val music_data = spark.sparkContext.textFile("D:\\hadoop\\sparkTest\\data\\music_meta.txt")
val stop_word = spark.sparkContext.textFile("D:\\hadoop\\sparkTest\\data\\stopwords.txt").collect()
val word_idf = spark.sparkContext.textFile("D:\\hadoop\\sparkTest\\data\\idf.txt")
val music_df = music_data.map(_.toString.trim.split("\t")).map(music =>Music(music(0).trim, music(1).trim)).toDF("music_id","music_content")
val word_idf_df = word_idf.map(_.toString.trim.split(" ")).map(line => IdfWords(line(0).trim,line(1).trim)).toDF("word_idf","idf")
//定义一个结巴分词的udf
def jieba_udf(df:DataFrame, colname:String) : DataFrame ={
val segmenter = new JiebaSegmenter()
val seg = spark.sparkContext.broadcast(segmenter)
val seg_udf = udf{music_content:String =>
val segV = seg.value
segV.process(music_content.toString,SegMode.INDEX)
.toArray()
.map(_.asInstanceOf[SegToken].word)
}
df.withColumn("words",seg_udf(col(colname)))
}
val seg_df = jieba_udf(music_df,"music_content").select("music_id","words")
seg_df.show(false)
//去停用词
val remover = new StopWordsRemover().setStopWords(stop_word).setInputCol("words").setOutputCol("filter_words")
val filter_seg_df = remover.transform(seg_df).select("music_id","filter_words")
filter_seg_df.show(false)
val explode_df = filter_seg_df.selectExpr("music_id","explode(filter_words) as word").where("word <> ' '")
.select("music_id","word")
explode_df.show(false)
val music_id_word_idf = explode_df.join(word_idf_df,explode_df("word")===word_idf_df("word_idf")).drop("word_idf")
//获取倒排索引,并排序
val word_group = music_id_word_idf.rdd.map(line => (line(1).toString,(line(0).toString,line(2).toString)))
.groupByKey().mapValues{x=>
x.toArray.sortWith((x,y) => x._2 > y._2).slice(0,10)
}.toDF("word","music_id_array").select("word","music_id_array")
word_group.show(false)
网友评论