Spark笔记(1) :余弦相似度计算

spark


余弦相似度

在推荐系统中,基于物品的协同过滤算法是业界应用最多的算法,它的思想是给用户推荐那些和他们喜欢的物品相似的物品,主要分为两个步骤:一,计算物品之间的相似度;二,根据物品相似度和用户的历史行为给用户生成推荐列表。

其中物品的相似度的计算,可以通过余弦相似度计算。余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。
计算公式如下:
cos(\theta ) = \frac{\sum_{1}^{n}(x_{i} * y_{i})}{\sqrt{\sum_{1}^{n}(x_{i})^{2}} * \sqrt{\sum_{1}^{n}(y_{i})^{2}}}

以文本相似度为例,用上述理论计算文本的相似性。

句子A:这只皮靴号码大了,那只号码合适
句子B:这只皮靴号码不小,那只更合适

怎样计算上面两句话的相似程度?

基本思路是:如果这两句话的用词越相似,它们的内容就应该越相似。因此,可以从词频入手,计算它们的相似程度。
第一步,分词

句子A:这只/皮靴/号码/大了/那只/号码/合适。
句子B:这只/皮靴/号码/不/小/那只/更/合适。

第二步,列出所有的词

这只,皮靴,号码,大了。那只,合适,不,小,很

第三步,计算词频

句子A:这只:1,皮靴:1,号码:2,大了:1,那只:1,合适:1,不:0,小:0,更:0
句子B:这只:1,皮靴:1,号码:1,大了:0,那只:1,合适:1,不:1,小:1,更:1

第四步,写出词频向量

句子A:(1,1,2,1,1,1,0,0,0)
句子B:(1,1,1,0,1,1,1,1,1)

问题就变成了如何计算这两个向量的相似程度。可以想象成空间中的两条线段,都是从原点出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为0度,意味着方向相同、线段重合,这是表示两个向量代表的文本完全相等;如果夹角为180度,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。
使用上面的公式计算可得:

分子:1*1 + 1*1 + 2*1 + 1*0 + 1*1 + 1*1 + 0*1 + 0*1 + 0*1 = 6 
分母:sqrt(1^2+1^2+2^2+1^2+1^2+1^2)*sqrt(1^2+1^2+1^2+1^2+1^2+1^2+1^2+1^2) = 7.483
句子AB相似度 = 6/7.483 =  0.81

计算结果中夹角的余弦值为0.81非常接近于1,所以,上面的句子A和句子B是基本相似的。

电影推荐计算用户相似度

spark例子:

package ALSdemo

import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable

object UserSimCalculationDemo {

  //屏蔽不必要的日志显示在终端上
  Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
  Logger.getLogger("org.apache.eclipse.jetty.server").setLevel(Level.OFF)

  def main(args: Array[String]) {
    val conf = new SparkConf().setMaster("local[1]").setAppName(this.getClass.getSimpleName.filter(!_.equals('$')))
    println(this.getClass.getSimpleName.filter(!_.equals('$')))
    val sc = new SparkContext(conf)
    //设置用户名
    val users = sc.parallelize(Array("张三", "李四", "王五", "赵六", "阿七"))
    //设置电影名
    val films = sc.parallelize(Array("逆战", "人间", "鬼屋", "西游记", "雪豹"))
    //初始化分数
    getSource

    val name1 = "张三"
    val name2 = "李四"
    val name3 = "王五"
    val name4 = "赵六"
    val name5 = "阿七"

    users.foreach(user => {
      println(name1 + " 相对于 " + user + " 的相似性分数是 " + getCollaborateSource(name1, user) )
      println("--------------------------------------------------------------------------")
    })

  }

  //使用一个source嵌套map作为姓名电影名和分值的存储
  val source = mutable.Map[String, mutable.Map[String, Int]]()

  /**
    * 造数据
    * @return
    */
  def getSource: mutable.Map[String, mutable.Map[String, Int]] = {
    //设置电影评分
    val user1FilmSource = mutable.Map("逆战" -> 2, "人间" -> 3, "鬼屋" -> 1, "西游记" -> 0, "雪豹" -> 1)
    val user2FilmSource = mutable.Map("逆战" -> 1, "人间" -> 2, "鬼屋" -> 2, "西游记" -> 1, "雪豹" -> 4)
    val user3FilmSource = mutable.Map("逆战" -> 2, "人间" -> 1, "鬼屋" -> 0, "西游记" -> 1, "雪豹" -> 4)
    val user4FilmSource = mutable.Map("逆战" -> 3, "人间" -> 2, "鬼屋" -> 0, "西游记" -> 5, "雪豹" -> 3)
    val user5FilmSource = mutable.Map("逆战" -> 5, "人间" -> 3, "鬼屋" -> 1, "西游记" -> 1, "雪豹" -> 2)

    //对人名进行储存
    source += ("张三" -> user1FilmSource)
    source += ("李四" -> user2FilmSource)
    source += ("王五" -> user3FilmSource)
    source += ("赵六" -> user4FilmSource)
    source += ("阿七" -> user5FilmSource)

    //返回嵌套map
    source
  }

  //两两计算分值,采用余弦相似性
  def getCollaborateSource(user1: String, user2: String): Double = {
    //获得1,2两个用户的评分
    val user1FilmSource = source(user1).values.toVector
    val user2FilmSource = source(user2).values.toVector
    println(user1+": "+user1FilmSource)
    println(user2+": "+user2FilmSource)

    //对公式部分分子进行计算
    val member = user1FilmSource.zip(user2FilmSource).map(d => d._1 * d._2).sum.toDouble
    //求出分母第一个变量值
    val temp1 = math.sqrt(user1FilmSource.map(num => {
      math.pow(num, 2)
    }).sum)
    //求出分母第二个变量值
    val temp2 = math.sqrt(user2FilmSource.map(num => {
      math.pow(num, 2)
    }).sum)
    //求出分母
    val denominator = temp1 * temp2
    //进行计算
    member / denominator
  }
}

运行结果:

张三: Vector(1, 0, 2, 1, 3)
张三: Vector(1, 0, 2, 1, 3)
张三 相对于 张三 的相似性分数是 0.9999999999999999
--------------------------------------------------------------------------
张三: Vector(1, 0, 2, 1, 3)
李四: Vector(2, 1, 1, 4, 2)
张三 相对于 李四 的相似性分数是 0.7089175569585667
--------------------------------------------------------------------------
张三: Vector(1, 0, 2, 1, 3)
王五: Vector(0, 1, 2, 4, 1)
张三 相对于 王五 的相似性分数是 0.6055300708194983
--------------------------------------------------------------------------
张三: Vector(1, 0, 2, 1, 3)
赵六: Vector(0, 5, 3, 3, 2)
张三 相对于 赵六 的相似性分数是 0.564932682866032
--------------------------------------------------------------------------
张三: Vector(1, 0, 2, 1, 3)
阿七: Vector(1, 1, 5, 2, 3)
张三 相对于 阿七 的相似性分数是 0.8981462390204985
--------------------------------------------------------------------------
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容