В чем разница между HashingTF и CountVectorizer в Spark?

Попытка сделать классификацию док в Spark. Я не уверен, что хеширует в HashingTF; он жертвует какой-либо точностью? Я в этом сомневаюсь, но не знаю. В искровом доке говорится, что он использует "хеширующий трюк"... просто еще один пример действительно плохого/запутанного наименования, используемого инженерами (я тоже виноват). CountVectorizer также требует установки размера словаря, но у него есть другой параметр - пороговый параметр, который можно использовать для исключения слов или токенов, которые появляются ниже некоторого порога в текстовом корпусе. Я не понимаю разницы между этими двумя Трансформаторами. Что делает это важным, это последующие шаги в алгоритме. Например, если бы я хотел выполнить SVD на полученной матрице tfidf, тогда размер словаря будет определять размер матрицы для SVD, что влияет на время работы кода, а также на производительность модели и т.д. У меня трудности вообще найти любой источник информации о Spark Mllib за пределами документации API и действительно тривиальные примеры без глубины.

Ответ 1

Несколько важных отличий:

  • частично обратимый (CountVectorizer) vs необратимый (HashingTF) - поскольку хеширование не является обратимым, вы не можете восстановить исходный вход из хеш-вектора. С другой стороны, счетчик векторов с моделью (индексом) можно использовать для восстановления неупорядоченного ввода. Как следствие, модели, созданные с использованием хешированного ввода, могут быть намного сложнее интерпретировать и контролировать.
  • память и вычислительные накладные расходы - HashingTF требуют только одного сканирования данных и дополнительной памяти за пределами исходного ввода и вектора. CountVectorizer требует дополнительного сканирования данных для создания модели и дополнительной памяти для хранения словаря (индекса). В случае модели языка униграмм это обычно не проблема, но в случае более высоких n-граммов она может быть чрезмерно дорогой или невыполнимой.
  • хеширование зависит от от размера вектора, хэширования и документа. Подсчет зависит от размера вектора, учебного корпуса и документа.
  • источник потери информации - в случае HashingTF это уменьшение размерности с возможными столкновениями. CountVectorizer отбрасывает нечетные жетоны. Как это влияет на модели ниже по потоку, зависит от конкретного варианта использования и данных.

Ответ 2

Согласно документации Spark 2.1.0,

  И HashingTF, и CountVectorizer могут использоваться для создания термина частотных векторов.

HashingTF

HashingTF - это Трансформер, который принимает наборы терминов и преобразует эти наборы в векторные векторы фиксированной длины. При обработке текста "набор терминов" может быть мешком слов. HashingTF использует трюк хеширования. Необработанный объект сопоставляется с индексом (термином) путем применения хэша  функция. Здесь используется хэш-функция MurmurHash 3. Тогда термин частоты рассчитываются на основе сопоставленных индексов. Этот подход избегает необходимости вычислять глобальную карту терминов для индекса, которая может быть дорого для большого корпуса, но страдает от потенциального хэша столкновения, в которых разные необработанные элементы могут стать одним и тем же термином после перемешивания.

Чтобы уменьшить вероятность столкновения, мы можем увеличить измерение целевого объекта, то есть количество сегментов хэша Таблица. Так как простой модуль используется для преобразования хеш-функции в индекс столбца, желательно использовать степень два в качестве функции измерение, в противном случае элементы не будут отображаться равномерно колонны. Размерность объекта по умолчанию: 2 ^ 18 = 262 144. необязательный параметр бинарного переключателя контролирует счетчик частоты терминов. когда установлен в true, все ненулевые счетчики частоты установлены в 1. Это особенно полезно для дискретных вероятностных моделей, которые моделируют двоичные, рассчитывает, а не целое число.

CountVectorizer

CountVectorizer и CountVectorizerModel призваны помочь преобразовать сбор текстовых документов по векторам количества токенов. Когда априорный словарь недоступен, CountVectorizer может использоваться как Оценщик, чтобы извлечь словарный запас и генерирует CountVectorizerModel. Модель дает разреженные представления для документы над словарем, которые затем могут быть переданы другим алгоритмы, такие как LDA.

В процессе подгонки CountVectorizer выберет верхнюю vocabSize слова, упорядоченные по частоте термина по всему корпусу. необязательный параметр minDF также влияет на процесс подбора указание минимального количества (или доли, если & lt; 1.0) документов a термин должен появиться, чтобы быть включенным в словарь. Еще один дополнительный двоичный параметр переключения управляет выходным вектором. Если установлено в 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)

Вывод как показано ниже

enter image description here

Хеширование 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

Ответ 3

Уловка хеширования - фактически другое название хеширования функций.

Я цитирую определение из Википедии:

В машинном обучении хеширование признаков, также известное как уловка хеширования, по аналогии с уловкой ядра, является быстрым и экономичным способом векторизации признаков, то есть превращения произвольных признаков в индексы в векторе или матрице. Он работает, применяя хеш-функцию к объектам и напрямую используя их значения хеш-функции в качестве индексов, вместо того, чтобы искать индексы в ассоциативном массиве.

Подробнее об этом можно прочитать в этой статье.

Так что на самом деле фактически для векторизации пространственно-эффективных функций.

Принимая во внимание, что CountVectorizer выполняет только извлечение словаря и преобразуется в векторы.

Ответ 4

Ответы отличные. Я просто хочу подчеркнуть эту разницу API:

Например,

 CountVectorizer(inputCol="words", outputCol="features")
      .fit(original_df)
      .transform(original_df)

против:

 HashingTF(inputCol="words", outputCol="features")
      .transform(original_df)

В этом отличии API CountVectorizer есть дополнительный шаг API fit. Может быть, это потому, что CountVectorizer выполняет дополнительную работу (см. принятый ответ):

CountVectorizer требует дополнительного сканирования данных для построения модели и дополнительной памяти для хранения словарного запаса (индекса).

Я думаю, что вы также можете пропустить подходящий шаг, если вы можете создать свой CountVectorizerModel напрямую, как показано в примере:

// alternatively, define CountVectorizerModel with a-priori vocabulary
val cvm = new CountVectorizerModel(Array("a", "b", "c"))
  .setInputCol("words")
  .setOutputCol("features")

cvModel.transform(df).show(false)

Еще одна большая разница!

  • HashingTF может создавать коллизии! Это означает, что две разные особенности/слова рассматриваются как один и тот же термин.
  • Принятый ответ гласит:

    источник потери информации - в случае HashingTF это уменьшение размерности с возможными коллизиями

Это особенно проблема с явным низким значением numFeatures (pow(2,4), pow(2,8)); значение по умолчанию довольно высокое (pow(2,20)) В этом примере:

wordsData = spark.createDataFrame([([
    'one', 'two', 'three', 'four', 'five', 
    'six',  'seven', 'eight', 'nine', 'ten'],)], ['tokens'])
hashing = HashingTF(inputCol="tokens", outputCol="hashedValues", numFeatures=pow(2,4))
hashed_df = hashing.transform(wordsData)
hashed_df.show(truncate=False)


+-----------------------------------------------------------+
|hashedValues                                               |
+-----------------------------------------------------------+
|(16,[0,1,2,6,8,11,12,13],[1.0,1.0,1.0,3.0,1.0,1.0,1.0,1.0])|
+-----------------------------------------------------------+

Вывод содержит 16 "блоков хеша" (потому что я использовал numFeatures=pow(2,4))

...16...

В то время как мой вход имел 10 уникальных токенов, выход создает только 8 уникальных хешей (из-за коллизий хешей);

....v-------8x-------v....
...[0,1,2,6,8,11,12,13]...

Хеш-коллизии означают, что 3 разных токена имеют одинаковый хеш (хотя все токены уникальны и должны встречаться только 1 раз)

...---------------v
... [1.0,1.0,1.0,3.0,1.0,1.0,1.0,1.0] ...

(Поэтому оставьте значение по умолчанию или увеличьте numFeatures, чтобы избежать коллизий :

Этот подход [хеширования] позволяет избежать необходимости вычислять глобальную карту терминов для индекса, которая может быть дорогостоящей для большого корпуса, но страдает от возможных коллизий хеша, когда различные необработанные объекты могут стать одним и тем же термином после хеширования. Чтобы уменьшить вероятность столкновения, мы можем увеличить размер целевого объекта, то есть количество сегментов хеш-таблицы.

Некоторые другие отличия API

  • Конструктор CountVectorizer (т.е. при инициализации) поддерживает дополнительные параметры:

    • minDF
    • minTF
    • и т.д...
  • CountVectorizerModel имеет члена vocabulary, поэтому вы можете увидеть сгенерированный vocabulary (особенно полезно, если вы fit ваш CountVectorizer):

    • countVectorizerModel.vocabulary
    • >>> [u'one', u'two', ...]
  • CountVectorizer является "обратимым", как говорит основной ответ! Используйте его член vocabulary, который является массивом, отображающим индекс термина, на термин (sklearn CountVectorizer делает нечто подобное)