在處理文本數據,尤其是自然語言處理的場景中,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方法。