性能奇怪的问题Spark LSH MinHash近似相似

时间:2018-07-18 13:47:44

标签: apache-spark duplicates apache-spark-mllib minhash lsh

我正在使用Apache Spark ML LSH的roximatedSimilarityJoin方法加入2个数据集,但是我看到一些奇怪的行为。

(内部)加入后,数据集有点偏斜,但是每次一个或多个任务需要花费非常长的时间才能完成。

sparkui-1

如您所见,每个任务的中位数为6毫秒(我正在较小的源数据集上对其进行测试),但是1个任务需要10分钟。它几乎不使用任何CPU周期,它实际上是在联接数据,但是太慢了。 下一个最慢的任务在14秒内运行,记录增加了4倍,实际上溢出到磁盘上。

如果您看着sparkuisql

联接本身是pos和hashValue(minhash)上的两个数据集之间的内部联接,符合minhash规范和udf,用于计算匹配对之间的jaccard距离。

分解哈希表:

modelDataset.select(
      struct(col("*")).as(inputName), posexplode(col($(outputCol))).as(explodeCols))

Jaccard距离功能:

 override protected[ml] def keyDistance(x: Vector, y: Vector): Double = {
    val xSet = x.toSparse.indices.toSet
    val ySet = y.toSparse.indices.toSet
    val intersectionSize = xSet.intersect(ySet).size.toDouble
    val unionSize = xSet.size + ySet.size - intersectionSize
    assert(unionSize > 0, "The union of two input sets must have at least 1 elements")
    1 - intersectionSize / unionSize
  }

加入已处理的数据集:

// Do a hash join on where the exploded hash values are equal.
val joinedDataset = explodedA.join(explodedB, explodeCols)
  .drop(explodeCols: _*).distinct()

// Add a new column to store the distance of the two rows.
val distUDF = udf((x: Vector, y: Vector) => keyDistance(x, y), DataTypes.DoubleType)
val joinedDatasetWithDist = joinedDataset.select(col("*"),
  distUDF(col(s"$leftColName.${$(inputCol)}"), col(s"$rightColName.${$(inputCol)}")).as(distCol)
)

// Filter the joined datasets where the distance are smaller than the threshold.
joinedDatasetWithDist.filter(col(distCol) < threshold)

我尝试过将缓存,重新分区甚至启用spark.speculation组合使用,但无济于事。

数据由必须匹配的带状地址文本组成: 53536, Evansville, WI => 53, 35, 36, ev, va, an, ns, vi, il, ll, le, wi 与城市或邮编中有错字的记录的距离将很短。

给出的结果非常准确,但这可能是联接偏斜的原因。

我的问题是:

  • 什么可能导致此差异? (即使一项记录较少,一项任务也需要很长时间)
  • 如何在不损失准确性的情况下防止minhash中的这种偏斜?
  • 是否有更好的方法可以大规模进行此操作? (我无法Jaro-Winkler / levenshtein将数百万条记录与位置数据集中的所有记录进行比较)

1 个答案:

答案 0 :(得分:1)

可能有点晚了,但是无论如何我都会在这里发布答案,以帮助他人。最近,我遇到了与拼写错误的公司名称(All executors dead MinHash LSH PySpark approxSimilarityJoin self-join on EMR cluster)相匹配的类似问题。有人建议我采用NGrams来减少数据偏差,从而帮助了我。这对我帮助很大。您也可以尝试使用3克或4克。

我不知道数据有多脏,但是您可以尝试利用状态。基本上已经减少了可能的匹配次数。

真正帮助我提高匹配准确性的是,通过在每个组件上运行标签传播算法来对连接的组件(MinHashLSH进行的一组连接的匹配)进行后处理。这还允许您增加N个(NGram中的)N,从而减轻数据偏斜的问题,减少approxSimilarityJoin中jaccard distance参数的设置,以及使用标签传播进行后处理。

最后,我目前正在研究使用skipgrams进行匹配。我发现在某些情况下它可以更好地工作,并在一定程度上减少了数据偏斜。