Gegeben 2 riesige Liste von Werten, ich versuche jaccard similarity zwischen ihnen in Spark mit Scala zu berechnen.Spark Jaccard Ähnlichkeitsberechnung durch min Hashing langsam im Vergleich zu trivialen Ansatz
Angenommen colHashed1
enthält die erste Liste von Werten und colHashed2
enthält die zweite Liste.
Ansatz 1 (Trivial Ansatz):
val jSimilarity = colHashed1.intersection(colHashed2).distinct.count/(colHashed1.union(colHashed2).distinct.count.toDouble)
Ansatz 2 (unter Verwendung von minHashing):
ich den Ansatz verwendet haben erklärt here.
import java.util.zip.CRC32
def getCRC32 (s : String) : Int =
{
val crc=new CRC32
crc.update(s.getBytes)
return crc.getValue.toInt & 0xffffffff
}
val maxShingleID = Math.pow(2,32)-1
def pickRandomCoeffs(kIn : Int) : Array[Int] =
{
var k = kIn
val randList = Array.fill(k){0}
while(k > 0)
{
// Get a random shingle ID.
var randIndex = (Math.random()*maxShingleID).toInt
// Ensure that each random number is unique.
while(randList.contains(randIndex))
{
randIndex = (Math.random()*maxShingleID).toInt
}
// Add the random number to the list.
k = k - 1
randList(k) = randIndex
}
return randList
}
val colHashed1 = list1Values.map(a => getCRC32(a))
val colHashed2 = list2Values.map(a => getCRC32(a))
val nextPrime = 4294967311L
val numHashes = 10
val coeffA = pickRandomCoeffs(numHashes)
val coeffB = pickRandomCoeffs(numHashes)
var signature1 = Array.fill(numHashes){0}
for (i <- 0 to numHashes-1)
{
// Evaluate the hash function.
val hashCodeRDD = colHashed1.map(ele => ((coeffA(i) * ele + coeffB(i)) % nextPrime))
// Track the lowest hash code seen.
signature1(i) = hashCodeRDD.min.toInt
}
var signature2 = Array.fill(numHashes){0}
for (i <- 0 to numHashes-1)
{
// Evaluate the hash function.
val hashCodeRDD = colHashed2.map(ele => ((coeffA(i) * ele + coeffB(i)) % nextPrime))
// Track the lowest hash code seen.
signature2(i) = hashCodeRDD.min.toInt
}
var count = 0
// Count the number of positions in the minhash signature which are equal.
for(k <- 0 to numHashes-1)
{
if(signature1(k) == signature2(k))
count = count + 1
}
val jSimilarity = count/numHashes.toDouble
Ansatz 1 scheint immer Ansatz 2 in Bezug auf die Zeit zu übertreffen. Wenn ich den Code analysierte, benötigt der Funktionsaufruf min()
auf dem RDD
in Approach 2 erhebliche Zeit, und diese Funktion wird oft aufgerufen, je nachdem wie viele Hash-Funktionen verwendet werden.
Die in Ansatz 1 verwendeten Schnitt- und Vereinigungsoperationen scheinen im Vergleich zu den wiederholten min() - Funktionsaufrufen schneller zu funktionieren.
Ich verstehe nicht, warum minHashing hier nicht hilft. Ich habe erwartet, dass minHashing im Vergleich zu trivialem Ansatz schneller arbeitet. Was mache ich hier falsch?
Beispieldaten können here
Können Sie Beispieldaten für Ihre hinzufügen col1 und col2 im Datensatz? – tuxdna
@tuxdna Beispieldatenlink hinzugefügt am Ende der Frage – CRM