【问题标题】:Strange performance issue Spark LSH MinHash approxSimilarityJoin奇怪的性能问题 Spark LSH MinHash approxSimilarityJoin
【发布时间】:2018-12-26 11:22:04
【问题描述】:

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

在(内部)加入之后,数据集有点倾斜,但是每次完成一个或多个任务都会花费过多的时间。

如您所见,每个任务的中位数是 6 毫秒(我在较小的源数据集上运行它来测试),但 1 个任务需要 10 分钟。它几乎不使用任何 CPU 周期,它实际上连接了数据,但速度如此之慢。 下一个最慢的任务在 14 秒内运行,记录多 4 倍,并且实际上溢出到磁盘。

如果你看

join本身是两个数据集在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 将数百万条记录与位置数据集中的所有记录进行比较)

【问题讨论】:

  • 你有解决办法吗
  • 是的,但可能不是您需要的 :-) 我处理了几次数据集。首先是东西完全匹配的默认连接。我在第二遍中过滤掉了那些,我使用简单的levenstein(等)方法来获得真正接近的方法。第三遍包含更少的数据并使用 LSH

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


【解决方案1】:

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

我不知道数据有多脏,但你可以尝试使用状态。它已经大大减少了可能匹配的数量。

真正帮助我提高匹配准确性的是通过在每个组件上运行标签传播算法对连接组件(由 MinHashLSH 生成的连接匹配组)进行后处理。这还允许您增加(NGrams 的)N,从而缓解数据倾斜问题,将approxSimilarityJoin 中的 jaccard 距离参数设置得不那么紧密,并使用标签传播进行后处理。

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

【讨论】:

  • 不错。我实际上已经使用了 n-gram,但连接组件标签传播可能是一个黄金主意。你为此使用了 GraphX 吗?不幸的是,我现在没有在做这个项目,但可能会在某个时候重新访问。
  • 不,实际上我现在正在用 python 做所有事情。所以我在 python 中使用了NetworkX 库,让它在本地为网络中的每个连接组件运行标签传播算法。我认为它也应该通过在整个网络上运行标签传播算法来工作,但我还没有尝试过。那时我会使用Graphframes,因为我现在正在使用python。一旦我尝试过,我会发布我的经验:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多