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

spark


余弦相似度

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

其中物品的相似度的计算,可以通过余弦相似度计算。余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。
计算公式如下:

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

句子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
--------------------------------------------------------------------------

你可能感兴趣的:(Spark笔记(1) :余弦相似度计算)