Spark中的HashingTF和CountVectorizer有什么区别?

Kai*_*Kai 22 apache-spark apache-spark-ml apache-spark-mllib

试图在Spark中进行doc分类.我不确定HashingTF中的散列是做什么的; 它会牺牲任何准确性吗?我对此表示怀疑,但我不知道.spark文档说它使用了"散列技巧"......只是工程师使用的另一个非常糟糕/混乱命名的例子(我也很内疚).CountVectorizer还需要设置词汇量大小,但它有另一个参数,一个阈值参数,可用于排除出现在文本语料库中某个阈值以下的单词或标记.我不明白这两个变形金刚之间的区别.使这一点很重要的是算法中的后续步骤.例如,如果我想在生成的tfidf矩阵上执行SVD,那么词汇量大小将决定SVD矩阵的大小,这会影响代码的运行时间,以及模型性能等.我一般有困难在API文档之外找到关于Spark Mllib的任何来源以及没有深度的非常简单的例子.

zer*_*323 23

一些重要的区别:

  • 部分可逆(CountVectorizer)vs不可逆(HashingTF) - 由于散列不可逆,因此无法从散列向量中恢复原始输入.另一方面,带有模型(索引)的计数向量可用于恢复无序输入.因此,使用散列输入创建的模型可能更难以解释和监视.
  • 内存和计算开销 - HashingTF只需要一次数据扫描,并且不需要超出原始输入和向量的额外内存.CountVectorizer需要对数据进行额外扫描以构建模型和额外的内存来存储词汇(索引).在unigram语言模型的情况下,它通常不是问题,但是在更高的n-gram的情况下,它可能过于昂贵或不可行.
  • 散列取决于向量的大小,散列函数和文档.计数取决于矢量,训练语料库和文档的大小.
  • 信息丢失的来源 - 如果是HashingTF这种情况,可以减少可能的碰撞.CountVectorizer丢弃不常见的令牌.它如何影响下游模型取决于特定的用例和数据.


pra*_*nth 13

根据Spark 2.1.0文档,

HashingTF和CountVectorizer都可用于生成术语频率向量.

HashingTF

HashingTF是一个Transformer,它接受一组术语并将这些集转换为固定长度的特征向量.在文本处理中,"一组术语"可能是一包单词.HashingTF利用散列技巧.通过应用散列函数将原始特征映射到索引(术语).这里使用的哈希函数是MurmurHash 3.然后,基于映射的索引计算术语频率.这种方法避免了计算全局术语到索引映射的需要,这对于大型语料库来说可能是昂贵的,但是它遭受潜在的哈希冲突,其中不同的原始特征可能在散列之后变成相同的术语.

为了减少冲突的可能性,我们可以增加目标要素维度,即哈希表的桶数.由于使用简单模来将散列函数转换为列索引,因此建议使用2的幂作为要素维,否则要素将不会均匀映射到列.默认要素尺寸为2 ^ 18 = 262,144.可选的二进制切换参数控制术语频率计数.设置为true时,所有非零频率计数都设置为1.这对于模拟二进制而非整数计数的离散概率模型特别有用.

CountVectorizer

CountVectorizer和CountVectorizerModel旨在帮助将文本文档集合转换为令牌计数向量.当a-priori字典不可用时,CountVectorizer可用作Estimator来提取词汇表,并生成CountVectorizerModel.该模型为词汇表上的文档生成稀疏表示,然后可以将其传递给其他算法,如LDA.

在拟合过程中,CountVectorizer将选择按语料库中的术语频率排序的顶级词汇量词.可选参数minDF还通过指定术语必须出现在文档中的最小数量(或<1.0)来影响拟合过程.另一个可选的二进制切换参数控制输出向量.如果设置为true,则所有非零计数都设置为1.这对于模拟二进制而非整数计数的离散概率模型尤其有用.

示例代码

from pyspark.ml.feature import HashingTF, IDF, Tokenizer
from pyspark.ml.feature import CountVectorizer

sentenceData = spark.createDataFrame([
    (0.0, "Hi I heard about Spark"),
    (0.0, "I wish Java could use case classes"),
    (1.0, "Logistic regression models are neat")],
 ["label", "sentence"])

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

hashingTF = HashingTF(inputCol="words", outputCol="Features", numFeatures=100)
hashingTF_model = hashingTF.transform(wordsData)
print "Out of hashingTF function"
hashingTF_model.select('words',col('Features').alias('Features(vocab_size,[index],[tf])')).show(truncate=False)


# fit a CountVectorizerModel from the corpus.
cv = CountVectorizer(inputCol="words", outputCol="Features", vocabSize=20)

cv_model = cv.fit(wordsData)

cv_result = model.transform(wordsData)
print "Out of CountVectorizer function"
cv_result.select('words',col('Features').alias('Features(vocab_size,[index],[tf])')).show(truncate=False)
print "Vocabulary from CountVectorizerModel is \n" + str(cv_model.vocabulary)
Run Code Online (Sandbox Code Playgroud)

输出如下

在此输入图像描述

Hashing TF错过了对LDA等技术至关重要的词汇表​​.为此,必须使用CountVectorizer功能.无论词汇大小如何,CountVectorizer函数估计术语频率而不涉及任何近似值,这与HashingTF不同.

参考:

https://spark.apache.org/docs/latest/ml-features.html#tf-idf

https://spark.apache.org/docs/latest/ml-features.html#countvectorizer

  • 您是什么意思哈希TF“缺少词汇”?在将不同的单词/标记映射到同一垃圾箱(功能)时可能会发生冲突,但不会“错过”。通过控制numFeatures参数可以最大程度地减少冲突次数。CV是精确的-不考虑稀有的排除令牌-。您可以进行字数统计,以了解numFeatures的合适值。我认为视情况而定,您可以使用任何一种尺寸的vocab。我看不出有什么大的不同。例如,如果您的应用程序的声带大小不断变化,CountVectorizer会为您完成一些工作。 (2认同)

eli*_*sah 5

哈希技巧实际上是功能哈希的另一个名称。

我在引用Wikipedia的定义:

在机器学习中,类似于内核技巧,特征哈希(也称为哈希技巧)是对特征进行矢量化的一种快速且节省空间的方法,即将任意特征转换为向量或矩阵中的索引。它通过对特征应用哈希函数并直接将其哈希值用作索引来工作,而不是在关联数组中查找索引。

您可以在本文中了解更多信息。

因此实际上实际上是为了空间高效的特征向量化。

CountVectorizer仅执行词汇提取并将其转换为Vector。

  • 是的,但是indexOf是对numFeatures进行哈希模运算(http://stackoverflow.com/a/31540959/1560062) (2认同)