web-dev-qa-db-ja.com

SparkのHashingTFとCountVectorizerの違いは何ですか?

Sparkでドキュメント分類をしようとしています。 HashingTFでハッシュが何を行うかはわかりません。精度を犠牲にしますか?私はそれを疑いますが、知りません。 spark docは、「ハッシュトリック」を使用していると言います...エンジニアが使用する本当に悪い/混乱するネーミングのもう1つの例です(私も有罪です)。CountVectorizerには語彙の設定も必要ですサイズですが、別のパラメーターがあります。テキストコーパスの特定のしきい値より下に表示される単語またはトークンを除外するために使用できるしきい値パラメーターです。これらの2つのトランスフォーマーの違いがわかりません。これが重要なのは、アルゴリズム。たとえば、結果のtfidf行列でSVDを実行したい場合、語彙のサイズによってSVDの行列のサイズが決まり、コードの実行時間やモデルのパフォーマンスなどに影響を与えます。 Spark Mllibについての一般的なソースをAPIドキュメントを超えて見つけるのは困難であり、深さのない非常に簡単な例です。

22
Kai

いくつかの重要な違い:

  • 部分的にreversibleCountVectorizervs irreversibleHashingTF)-ハッシュは元に戻せないため、ハッシュベクトルから元の入力を復元することはできません。一方、モデル(インデックス)を持つカウントベクトルは、順序付けられていない入力を復元するために使用できます。結果として、ハッシュされた入力を使用して作成されたモデルは、解釈および監視がはるかに困難になる可能性があります。
  • メモリと計算のオーバーヘッド-HashingTFは、単一のデータスキャンのみを必要とし、元の入力とベクトル以外に追加のメモリは必要ありません。 CountVectorizerでは、モデルを構築するためにデータをさらにスキャンし、語彙(インデックス)を保存するために追加のメモリが必要です。ユニグラム言語モデルの場合、それは通常問題ではありませんが、より高いNグラムの場合、それは法外に高価であるか、または実行不可能である可能性があります。
  • ハッシュ依存ベクトルのサイズ、ハッシュ関数、およびドキュメント。カウントは、ベクトル、トレーニングコーパス、およびドキュメントのサイズに依存します。
  • 情報損失の原因-HashingTFの場合、衝突の可能性がある次元削減です。 CountVectorizerは、頻度の低いトークンを破棄します。ダウンストリームモデルにどのように影響するかは、特定のユースケースとデータによって異なります。
24
zero323

Spark 2.1.0ドキュメントに従って、

HashingTFとCountVectorizerの両方を使用して、用語周波数ベクトルを生成できます。

HashingTF

HashingTFは、用語のセットを取り、それらのセットを固定長の特徴ベクトルに変換するトランスフォーマーです。テキスト処理では、「用語のセット」は単語のバッグかもしれません。 HashingTFはハッシュトリックを利用しています。ハッシュ関数を適用することにより、生の特徴がインデックス(用語)にマップされます。ここで使用されるハッシュ関数はMurmurHash 3です。次に、マップされたインデックスに基づいて項頻度が計算されます。このアプローチでは、大規模なコーパスではコストが高くなる可能性があるグローバルな用語からインデックスへのマップを計算する必要がなくなりますが、ハッシュの衝突の可能性があり、ハッシュ後に異なる生の特徴が同じ用語になる可能性があります

衝突の可能性を減らすために、ターゲットフィーチャーの次元、つまりハッシュテーブルのバケットの数を増やすことができます。単純なモジュロを使用してハッシュ関数を列インデックスに変換するため、特徴の次元として2の累乗を使用することをお勧めします。そうしないと、特徴が列に均等にマッピングされません。デフォルトのフィーチャー寸法は2 ^ 18 = 262,144です。オプションのバイナリトグルパラメーターは、用語の頻度カウントを制御します。 trueに設定すると、ゼロ以外のすべての頻度カウントが1に設定されます。これは、整数ではなくバイナリカウントをモデル化する離散確率モデルで特に役立ちます。

CountVectorizer

CountVectorizerおよびCountVectorizerModelの目的は、テキストドキュメントのコレクションをトークンカウントのベクトルに変換することです。アプリオリ辞書が利用できない場合、CountVectorizerをEstimatorとして使用してvocabularyを抽出し、CountVectorizerModelを生成できます。モデルは、ドキュメントのスパース表現over the vocabularyを生成し、LDAなどの他のアルゴリズムに渡すことができます。

フィッティングプロセス中に、CountVectorizerは、コーパス全体の用語頻度で並べ替えられた上位のvocabSize単語を選択します。オプションのパラメーター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)

出力は以下のようになります

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

13
prashanth

ハッシュトリックは、実際には機能ハッシュの別名です。

私はウィキペディアの定義を引用しています:

機械学習では、ハッシュトリックとも呼ばれる特徴ハッシュは、カーネルトリックに類似しており、特徴をベクトル化する高速でスペース効率の良い方法です。つまり、任意の特徴をベクトルまたは行列のインデックスに変換します。これは、連想配列でインデックスを調べるのではなく、ハッシュ関数を機能に適用し、ハッシュ値をインデックスとして直接使用することで機能します。

詳細は この論文 をご覧ください。

したがって、実際にはスペース効率の良い機能のベクトル化のために。

一方、CountVectorizerは語彙抽出のみを実行し、ベクターに変換します。

5
eliasah

答えは素晴らしいです。このAPIの違いを強調したいだけです。

例えば

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

対:

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

このAPIの違いでは、CountVectorizerには追加のfit AP​​Iステップがあります。多分これは、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衝突を引き起こす可能性があります!つまり、2つの異なる特徴/単語は同じ用語として扱われます。
  • 受け入れられた答えはこれを言います:

    情報損失の原因-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 your CountVectorizer):

    • _countVectorizerModel.vocabulary_
    • _>>> [u'one', u'two', ...]_
  • CountVectorizerは、主な答えが言うように「リバーシブル」です!インデックスを用語にマップする配列であるそのvocabularyメンバーを用語 ((sklearn 's CountVectorizerは何か似ている) を使用します
1
The Red Pea