Spark HashingTF的工作原理

时间:2017-02-16 20:17:25

标签: apache-spark pyspark apache-spark-mllib tf-idf apache-spark-ml

我是Spark 2的新手。 我试过Spark tfidf例子

sentenceData = spark.createDataFrame([
    (0.0, "Hi I heard about Spark")
], ["label", "sentence"])

tokenizer = Tokenizer(inputCol="sentence", outputCol="words")
wordsData = tokenizer.transform(sentenceData)


hashingTF = HashingTF(inputCol="words", outputCol="rawFeatures", numFeatures=32)
featurizedData = hashingTF.transform(wordsData)

for each in featurizedData.collect():
    print(each)

输出

Row(label=0.0, sentence=u'Hi I heard about Spark', words=[u'hi', u'i', u'heard', u'about', u'spark'], rawFeatures=SparseVector(32, {1: 3.0, 13: 1.0, 24: 1.0}))

我希望在rawFeatures中,我会得到像{0:0.2, 1:0.2, 2:0.2, 3:0.2, 4:0.2}这样的字词频率。因为术语频率是:

tf(w) = (Number of times the word appears in a document) / (Total number of words in the document)

在我们的案例中,每个单词都有tf(w) = 1/5 = 0.2,因为每个单词在文档中都会出现一次。 如果我们想象输出rawFeatures字典包含单词索引作为键,以及文档中单词出现的数量作为值,为什么键1等于3.0?文档中没有出现3次单词。 这对我来说很困惑。我错过了什么?

1 个答案:

答案 0 :(得分:2)

TL; DR; 这只是一个简单的哈希冲突。 HashingTF需要hash(word) % numBuckets来确定存储桶,并且桶的数量非常少,就像这里预计会发生冲突一样。一般来说,你应该使用更多数量的桶,或者如果碰撞是不可接受的,CountVectorizer

详细说明。 HashingTF默认使用Murmur哈希。 [u'hi', u'i', u'heard', u'about', u'spark']将被[-537608040, -1265344671, 266149357, 146891777, 2101843105]散列。如果你follow the source,你会发现实现等同于:

import org.apache.spark.unsafe.types.UTF8String
import org.apache.spark.unsafe.hash.Murmur3_x86_32.hashUnsafeBytes

Seq("hi", "i", "heard", "about", "spark")
  .map(UTF8String.fromString(_))
  .map(utf8 => 
    hashUnsafeBytes(utf8.getBaseObject, utf8.getBaseOffset, utf8.numBytes, 42))
Seq[Int] = List(-537608040, -1265344671, 266149357, 146891777, 2101843105)

当您获取non-negative modulo这些值时,您将获得[24, 1, 13, 1, 1]

List(-537608040, -1265344671, 266149357, 146891777, 2101843105)
  .map(nonNegativeMod(_, 32))
List[Int] = List(24, 1, 13, 1, 1)

列表中的三个单词(i,about和spark)散列到同一个桶中,每个单词出现一次,因此得到的结果。

相关: