Dato 2 elenco enorme di valori, sto provando a calcolare jaccard similarity tra di loro in Spark usando Scala.Calcolo della similarità di Spark Jaccard per hashing min lento rispetto all'approccio banale
Si supponga che colHashed1
contenga il primo elenco di valori e colHashed2
contenga il secondo elenco.
Approccio 1 (approccio banale):
val jSimilarity = colHashed1.intersection(colHashed2).distinct.count/(colHashed1.union(colHashed2).distinct.count.toDouble)
Approccio 2 (utilizzando minHashing):
Ho usato il metodo spiegato 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
Approccio 1 sembra a sovraperformare Approccio 2 in termini di tempo sempre. Quando ho analizzato il codice, la chiamata di funzione min()
nello RDD
in Approccio 2 richiede un tempo significativo e tale funzione viene chiamata più volte in base a quante funzioni hash vengono utilizzate.
Le operazioni di intersezione e unione utilizzate in Approccio 1 sembrano funzionare più velocemente rispetto alle ripetute chiamate di funzione min().
Non capisco perché minHashing non aiuti qui. Mi aspettavo che minHashing funzionasse più velocemente rispetto all'approccio banale. C'è qualcosa che sto facendo di sbagliato qui?
dati campione possono essere visualizzate here
si può aggiungere dati di esempio per la vostra col1 e col2 nel set di dati? – tuxdna
@tuxdna collegamento dati campione aggiunto alla fine della domanda – CRM