Spark Mllib里相似度度量(基於余弦相似度計算不同用戶之間相似性)(圖文詳解)


 

 

  不多說,直接上干貨!

 

 

 

常見的推薦算法

  1、基於關系規則的推薦

  2、基於內容的推薦

  3、人口統計式的推薦

  4、協調過濾式的推薦

 

 

 

  協調過濾算法,是一種基於群體用戶或者物品的典型推薦算法,也是目前常用的推薦算法中最常用和最經典的算法。

   協調過濾算法主要有兩種:

    用戶對物品:  考查具有相同愛好的用戶相同物品的評分標准進行計算;

    物品對用戶:  考查具有相同物質的物品從而推薦選擇了某件物品的用戶。

 

 

 

 

 

相似度度量(基於歐幾里得距離的相似度計算和基於余弦角度的相似度計算)

    (1)、基於歐幾里得距離的相似度計算

        歐幾里得,是三維空間中兩個點的真實距離。

        歐幾里得相似度計算是一種基於用戶之間直線距離的計算方式。在相似度計算中,不同的物品或者用戶可以將其定義為不同的坐標點,而特定目標定位為坐標原點

        在實際應用中,常常使用歐幾里得距離的倒數作為相似度,即 1/ (d+1) 作為近似值。

 

    (2)、基於余弦角度的相似度計算    

        不同的物品或者用戶作為不同的坐標點,但不能為坐標原點。

 

 

 

 

   其實,還有其他的算法,也可以來相似度度量

 

 

 

 

 

 

 

 

  

 

基於歐幾里得距離的相似度計算和基於余弦角度的相似度計算的區別

  歐幾里得相似度是以目標絕對距離作為衡量的標准,余弦相似度以目標差異的大小作為衡量標准。

  歐幾里得相似度注重目標之間的差異,與目標在空間中的位置直接相關。余弦相似度是不同目標在空間中的夾角,更加表現在前進趨勢上的差異。

  歐幾里得相似度和余弦相似度具有不同的計算方法和描述特征,一般來說,歐幾里得相似度用來表現不同目標的絕對差異性,分析目標之間的相似度與差異情況。而余弦相似度更多的是對目標從方向趨勢上區分,對特定坐標數字不敏感。

 

 

 

 

 

基於余弦相似度計算不同用戶之間相似性

  步驟是:  

  (1)輸入數據  

  (2)建立相似度算法公式

  (3)計算不同用戶之間的相似度

 

 

 

 

 

 

 

 

 

   CollaborativeFilteringSpark.scala

package zhouls.bigdata.chapter5


import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.Map


object CollaborativeFilteringSpark {
  val conf = new SparkConf().setMaster("local").setAppName("CollaborativeFilteringSpark ")    //設置環境變量
  val sc = new SparkContext(conf)                                 //實例化環境
  val users = sc.parallelize(Array("aaa","bbb","ccc","ddd","eee"))       //設置用戶
  val films = sc.parallelize(Array("smzdm","ylxb","znh","nhsc","fcwr"))    //設置電影名
   
  val source = Map[String,Map[String,Int]]()                //使用一個source嵌套map作為姓名電影名和分值的存儲
   val filmSource = Map[String,Int]()                     //設置一個用以存放電影分的map
   def getSource(): Map[String,Map[String,Int]] = {            //設置電影評分
     val user1FilmSource = Map("smzdm" -> 2,"ylxb" -> 3,"znh" -> 1,"nhsc" -> 0,"fcwr" -> 1)
     val user2FilmSource = Map("smzdm" -> 1,"ylxb" -> 2,"znh" -> 2,"nhsc" -> 1,"fcwr" -> 4)
     val user3FilmSource = Map("smzdm" -> 2,"ylxb" -> 1,"znh" -> 0,"nhsc" -> 1,"fcwr" -> 4)
     val user4FilmSource = Map("smzdm" -> 3,"ylxb" -> 2,"znh" -> 0,"nhsc" -> 5,"fcwr" -> 3)
     val user5FilmSource = Map("smzdm" -> 5,"ylxb" -> 3,"znh" -> 1,"nhsc" -> 1,"fcwr" -> 2)
     source += ("aaa" -> user1FilmSource)                //對人名進行存儲
     source += ("bbb" -> user2FilmSource)                 //對人名進行存儲
     source += ("ccc" -> user3FilmSource)                 //對人名進行存儲
     source += ("ddd" -> user4FilmSource)                 //對人名進行存儲
     source += ("eee" -> user5FilmSource)                 //對人名進行存儲
     source                                        //返回嵌套map
   }

   //兩兩計算分值,采用余弦相似性
   def getCollaborateSource(user1:String,user2:String):Double = {
     val user1FilmSource = source.get(user1).get.values.toVector        //獲得第1個用戶的評分
     val user2FilmSource = source.get(user2).get.values.toVector        //獲得第2個用戶的評分
     val member = user1FilmSource.zip(user2FilmSource).map(d => d._1 * d._2).reduce(_ + _).toDouble            //對公式分子部分進行計算
     val temp1  = math.sqrt(user1FilmSource.map(num => {            //求出分母第1個變量值
       math.pow(num,2)                                        //數學計算          
     }).reduce(_ + _))                                        //進行疊加
     val temp2  = math.sqrt(user2FilmSource.map(num => {            ////求出分母第2個變量值
       math.pow(num,2)                                     //數學計算
     }).reduce(_ + _))                                        //進行疊加
     val denominator = temp1 * temp2                            //求出分母
     member / denominator                                    //進行計算
}

   def main(args: Array[String]) {
     getSource()                                            //初始化分數
     val name = "bbb"                                        //設定目標對象
     users.foreach(user =>{                                    //迭代進行計算
     println(name + " 相對於 " + user +"的相似性分數是:"+ getCollaborateSource(name,user))
    })
   }
 }

 

 

 

 

bbb 相對於 aaa的相似性分數是:0.7089175569585667
bbb 相對於 bbb的相似性分數是:1.0000000000000002
bbb 相對於 ccc的相似性分數是:0.8780541105074453
bbb 相對於 ddd的相似性分數是:0.6865554812287477
bbb 相對於 eee的相似性分數是:0.6821910402406466

 

 

 

 

 

 

 

  當然,這里大家也可以進一步修改程序。

   CollaborativeFilteringSpark.scala

package zhouls.bigdata.chapter5


import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.Map


object CollaborativeFilteringSpark {
  val conf = new SparkConf().setMaster("local").setAppName("CollaborativeFilteringSpark ")    //設置環境變量
  val sc = new SparkContext(conf)                                 //實例化環境
  val users = sc.parallelize(Array("aaa","bbb","ccc","ddd","eee"))       //設置用戶
  val films = sc.parallelize(Array("smzdm","ylxb","znh","nhsc","fcwr"))    //設置電影名
   
  val source = Map[String,Map[String,Int]]()                //使用一個source嵌套map作為姓名電影名和分值的存儲
   val filmSource = Map[String,Int]()                     //設置一個用以存放電影分的map
   def getSource(): Map[String,Map[String,Int]] = {            //設置電影評分
     val user1FilmSource = Map("smzdm" -> 2,"ylxb" -> 3,"znh" -> 1,"nhsc" -> 0,"fcwr" -> 1)
     val user2FilmSource = Map("smzdm" -> 1,"ylxb" -> 2,"znh" -> 2,"nhsc" -> 1,"fcwr" -> 4)
     val user3FilmSource = Map("smzdm" -> 2,"ylxb" -> 1,"znh" -> 0,"nhsc" -> 1,"fcwr" -> 4)
     val user4FilmSource = Map("smzdm" -> 3,"ylxb" -> 2,"znh" -> 0,"nhsc" -> 5,"fcwr" -> 3)
     val user5FilmSource = Map("smzdm" -> 5,"ylxb" -> 3,"znh" -> 1,"nhsc" -> 1,"fcwr" -> 2)
     source += ("aaa" -> user1FilmSource)                //對人名進行存儲
     source += ("bbb" -> user2FilmSource)                 //對人名進行存儲
     source += ("ccc" -> user3FilmSource)                 //對人名進行存儲
     source += ("ddd" -> user4FilmSource)                 //對人名進行存儲
     source += ("eee" -> user5FilmSource)                 //對人名進行存儲
     source                                        //返回嵌套map
   }

   //兩兩計算分值,采用余弦相似性
   def getCollaborateSource(user1:String,user2:String):Double = {
     val user1FilmSource = source.get(user1).get.values.toVector        //獲得第1個用戶的評分
     val user2FilmSource = source.get(user2).get.values.toVector        //獲得第2個用戶的評分
     val member = user1FilmSource.zip(user2FilmSource).map(d => d._1 * d._2).reduce(_ + _).toDouble            //對公式分子部分進行計算
     val temp1  = math.sqrt(user1FilmSource.map(num => {            //求出分母第1個變量值
       math.pow(num,2)                                        //數學計算          
     }).reduce(_ + _))                                        //進行疊加
     val temp2  = math.sqrt(user2FilmSource.map(num => {            ////求出分母第2個變量值
       math.pow(num,2)                                     //數學計算
     }).reduce(_ + _))                                        //進行疊加
     val denominator = temp1 * temp2                            //求出分母
     member / denominator                                    //進行計算
}

   def main(args: Array[String]) {
     getSource()                                            //初始化分數
     var name = "bbb"                                        //設定目標對象
     users.foreach(user =>{                                    //迭代進行計算
     println(name + " 相對於 " + user +"的相似性分數是:"+ getCollaborateSource(name,user))
    })
    
    println()
    name = "aaa"     
    users.foreach(user => {
      //迭代進行計算
      println(name + " 相對於 " + user + "的相似性分數是:" + getCollaborateSource(name, user))
    })
    
   }
 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  或者,也可以寫下程序,如下

UserSimilar.scala
package zhouls.bigdata

import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkContext, SparkConf}
import scala.collection.mutable.Map

object UserSimilar {

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

  //程序入口
  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("逆戰", "人間", "鬼屋", "西游記", "雪豹"))

  //使用一個source嵌套map作為姓名電影名和分值的存儲
  val source = Map[String, Map[String, Int]]()

  //設置一個用以存放電影分的map
  val filmSource = Map[String, Int]()


  def getSource(): Map[String, Map[String, Int]] = {
    //設置電影評分
    val user1FilmSource = Map("逆戰" -> 2, "人間" -> 3, "鬼屋" -> 1, "西游記" -> 0, "雪豹" -> 1)
    val user2FilmSource = Map("逆戰" -> 1, "人間" -> 2, "鬼屋" -> 2, "西游記" -> 1, "雪豹" -> 4)
    val user3FilmSource = Map("逆戰" -> 2, "人間" -> 1, "鬼屋" -> 0, "西游記" -> 1, "雪豹" -> 4)
    val user4FilmSource = Map("逆戰" -> 3, "人間" -> 2, "鬼屋" -> 0, "西游記" -> 5, "雪豹" -> 3)
    val user5FilmSource = 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.get(user1).get.values.toVector
    val user2FilmSource = source.get(user2).get.values.toVector

    //對公式部分分子進行計算
    val member = user1FilmSource.zip(user2FilmSource).map(d => d._1 * d._2).reduce(_ + _).toDouble
    //求出分母第一個變量值
    val temp1 = math.sqrt(user1FilmSource.map(num => {math.pow(num, 2)}).reduce(_ + _))
    //求出分母第二個變量值
    val temp2 = math.sqrt(user2FilmSource.map(num => {math.pow(num, 2)}).reduce(_ + _))
    //求出分母
    val denominator = temp1 * temp2
    //進行計算
    member / denominator
  }

  def main(args: Array[String]) {
    //初始化分數
    getSource()

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

    users.foreach(user => {
      println(name1 + " 相對於 " + user + " 的相似性分數是 " + getCollaborateSource(name1, user) )
    })

    println("--------------------------------------------------------------------------")

    users.foreach(user => {
      println(name2 + " 相對於 " + user + " 的相似性分數是 " + getCollaborateSource(name2, user) )
    })

    println("--------------------------------------------------------------------------")

    users.foreach(user => {
      println(name3 + " 相對於 " + user + " 的相似性分數是 " + getCollaborateSource(name3, user) )
    })

    println("--------------------------------------------------------------------------")

    users.foreach(user => {
      println(name4 + " 相對於 " + user + " 的相似性分數是 " + getCollaborateSource(name4, user) )
    })

    println("--------------------------------------------------------------------------")

    users.foreach(user => {
      println(name5 + " 相對於 " + user + " 的相似性分數是 " + getCollaborateSource(name5, user) )
    })

  }

}

 

 

 

 

 

  結果是

"C:\Program Files\Java\jdk1.8.0_77\bin\java" -Didea.launcher.port=7534 "-Didea.launcher.bin.path=D:\Program Files (x86)\JetBrains\IntelliJ IDEA 15.0.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_77\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_77\jre\lib\rt.jar;G:\location\spark-mllib\out\production\spark-mllib;C:\Program Files (x86)\scala\lib\scala-actors-migration.jar;C:\Program Files (x86)\scala\lib\scala-actors.jar;C:\Program Files (x86)\scala\lib\scala-library.jar;C:\Program Files (x86)\scala\lib\scala-reflect.jar;C:\Program Files (x86)\scala\lib\scala-swing.jar;G:\home\download\spark-1.6.1-bin-hadoop2.6\lib\datanucleus-api-jdo-3.2.6.jar;G:\home\download\spark-1.6.1-bin-hadoop2.6\lib\datanucleus-core-3.2.10.jar;G:\home\download\spark-1.6.1-bin-hadoop2.6\lib\datanucleus-rdbms-3.2.9.jar;G:\home\download\spark-1.6.1-bin-hadoop2.6\lib\spark-1.6.1-yarn-shuffle.jar;G:\home\download\spark-1.6.1-bin-hadoop2.6\lib\spark-assembly-1.6.1-hadoop2.6.0.jar;G:\home\download\spark-1.6.1-bin-hadoop2.6\lib\spark-examples-1.6.1-hadoop2.6.0.jar;D:\Program Files (x86)\JetBrains\IntelliJ IDEA 15.0.5\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain mllib.CollaborativeFilteringSpark  
CollaborativeFilteringSpark  
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties  
SLF4J: Class path contains multiple SLF4J bindings.  
SLF4J: Found binding in [jar:file:/G:/home/download/spark-1.6.1-bin-hadoop2.6/lib/spark-assembly-1.6.1-hadoop2.6.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]  
SLF4J: Found binding in [jar:file:/G:/home/download/spark-1.6.1-bin-hadoop2.6/lib/spark-examples-1.6.1-hadoop2.6.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]  
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.  
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]  
16/08/04 20:41:49 INFO Slf4jLogger: Slf4jLogger started  
16/08/04 20:41:49 INFO Remoting: Starting remoting  
16/08/04 20:41:49 INFO Remoting: Remoting started; listening on addresses :[akka.tcp://sparkDriverActorSystem@192.168.1.100:8380]  
張三 相對於 張三 的相似性分數是 0.9999999999999999  
張三 相對於 李四 的相似性分數是 0.7089175569585667  
張三 相對於 王五 的相似性分數是 0.6055300708194983  
張三 相對於 趙六 的相似性分數是 0.564932682866032  
張三 相對於 阿七 的相似性分數是 0.8981462390204985  
--------------------------------------------------------------------------  
李四 相對於 張三 的相似性分數是 0.7089175569585667  
李四 相對於 李四 的相似性分數是 1.0000000000000002  
李四 相對於 王五 的相似性分數是 0.8780541105074453  
李四 相對於 趙六 的相似性分數是 0.6865554812287477  
李四 相對於 阿七 的相似性分數是 0.6821910402406466  
--------------------------------------------------------------------------  
王五 相對於 張三 的相似性分數是 0.6055300708194983  
王五 相對於 李四 的相似性分數是 0.8780541105074453  
王五 相對於 王五 的相似性分數是 1.0  
王五 相對於 趙六 的相似性分數是 0.7774630169639036  
王五 相對於 阿七 的相似性分數是 0.7416198487095662  
--------------------------------------------------------------------------  
趙六 相對於 張三 的相似性分數是 0.564932682866032  
趙六 相對於 李四 的相似性分數是 0.6865554812287477  
趙六 相對於 王五 的相似性分數是 0.7774630169639036  
趙六 相對於 趙六 的相似性分數是 1.0  
趙六 相對於 阿七 的相似性分數是 0.738024966423108  
--------------------------------------------------------------------------  
阿七 相對於 張三 的相似性分數是 0.8981462390204985  
阿七 相對於 李四 的相似性分數是 0.6821910402406466  
阿七 相對於 王五 的相似性分數是 0.7416198487095662  
阿七 相對於 趙六 的相似性分數是 0.738024966423108  
阿七 相對於 阿七 的相似性分數是 0.9999999999999998  
16/08/04 20:41:51 INFO RemoteActorRefProvider$RemotingTerminator: Shutting down remote daemon.  
  
Process finished with exit code 0  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM