spark mllib HashingTF解析

在處理文本數據,尤其是自然語言處理的場景中,hashingTF使用的比較多;
Mllib使用hashing trick實現詞頻。元素的特徵應用一個hash`函數映射到一個索引(即詞),通過這個索引計算詞頻。這個方法避免計算全局的詞-索引映射,因爲全局的詞-索引映射在大規模語料中花費較大。
但是,它會出現哈希衝突,這是因爲不同的元素特徵可能得到相同的哈希值。爲了減少碰撞衝突,我們可以增加目標特徵的維度,例如哈希表的桶數量。默認的特徵維度是1048576。
1、spark ML中使用的hash方法基本上都是murmurhash實現,
private var binary = false
private var hashAlgorithm = HashingTF.Murmur3
// math.pow(2,20)=1048576 代表hashingTF中能表徵的特徵個數
def this() = this(1 << 20)
private[spark] val seed = 42

2、獲取hash的方法

/**
   * Returns the index of the input term.
   */
  @Since("1.1.0")
  def indexOf(term: Any): Int = {
    Utils.nonNegativeMod(getHashFunction(term), numFeatures)
  }
/**
   用mur hash計算輸入特徵的hash code
   */
  private[spark] def murmur3Hash(term: Any): Int = {
    term match {
      case null => seed
      case b: Boolean => hashInt(if (b) 1 else 0, seed)
      case b: Byte => hashInt(b, seed)
      case s: Short => hashInt(s, seed)
      case i: Int => hashInt(i, seed)
      case l: Long => hashLong(l, seed)
      case f: Float => hashInt(java.lang.Float.floatToIntBits(f), seed)
      case d: Double => hashLong(java.lang.Double.doubleToLongBits(d), seed)
      case s: String =>
        val utf8 = UTF8String.fromString(s)
        hashUnsafeBytes(utf8.getBaseObject, utf8.getBaseOffset, utf8.numBytes(), seed)
      case _ => throw new SparkException("HashingTF with murmur3 algorithm does not " +
        s"support type ${term.getClass.getCanonicalName} of input data.")
    }
  }
  
/* *
對返回的hash code取模 保證特徵的索引在numFeatures範圍內
  */
  def nonNegativeMod(x: Int, mod: Int): Int = {
    val rawMod = x % mod
    rawMod + (if (rawMod < 0) mod else 0)
  }

所有輸入的特徵統一有該方法返回特徵對應的hash索引,用於解決大數據量的計算壓力;
Tips:
1)、當你使用HashingTF和IDF訓練完模型後,一定要保存你的IDFModel,還有HashingTF的參數,當後續你使用模型的時候
需要使用HashingTF相同的參數和模型生成時的同一個IDFModel,比如在spark-streaming中,切記!
2)、切記對自己語料庫中特徵數量要有預估,爲了減少碰撞,將numFeatures設置爲1048576

3、重點的實現過程

def transform(document: Iterable[_]): Vector = {
    val termFrequencies = mutable.HashMap.empty[Int, Double]
    // 返回特徵的出現次數 經典的wordcount
    val setTF = if (binary) (i: Int) => 1.0 else (i: Int) => termFrequencies.getOrElse(i, 0.0) + 1.0
    val hashFunc: Any => Int = getHashFunction
    document.foreach { term =>
      val i = Utils.nonNegativeMod(hashFunc(term), numFeatures)
      termFrequencies.put(i, setTF(i))
    }
    Vectors.sparse(numFeatures, termFrequencies.toSeq)
  }

計算單個預料中的每個特徵的出現次數

舉例子:

 val nGram = new NGram().setN(3).setInputCol("l_behavior").setOutputCol("elements")
 val nGramDF = nGram.transform(leftDF)
 val hashingTF = new HashingTF().
      setInputCol("elements").
      setOutputCol("features").
      setNumFeatures(1500000)
 val hashingDF = hashingTF.transform(nGramDF)

以上就是spark ML中HashingTF的實現;其數據類型爲具有iterator性質的,一般會把單個預料切分成Array[String]。
這個實現比較簡單,後續有時間會更新CountVectorizer;
HashingTF沒有保留原有語料庫中的原始的詞語。記住這點才能更好的區分後續的transform feature方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章