前陣子做了一些IT opreation analysis的research,從產線上取了一些J2EE server運行狀態的數據(CPU,Menory...),打算通過訓練JVM的數據來建立分類模型,用於server狀態的分類。這個過程中發現最難的地方就是構建訓練數據集,訓練數據必須要有明確的type flag,用以表示數據向量采集當時,server所處的狀態類別。簡單的說,就是大家不清楚哪些數據代表正常,哪些數據代表異常,哪些數據代表臨界狀態,甚至不知道server應該有幾種明確的狀態。出現這樣的狀況,是因為我們不是產線運維人員,沒有相關的經驗,對着一坨數據,兩眼一抹黑。好在我們有每條數據記錄的timestamp,唯一可以於外界聯系上的數據,我們就想着通過產線發生Incident的運維信息來大致把產線問題分成若干類,然后根據發生Incident的時間,對應數據的timestamp,從而定義數據的type flag。
因此我們就嘗試着從大量的Incident數據中提取出主要的引發Incident的原因。Incident的原因描述是一線運維人員在發現問題和解決問題過程中通過人工填寫的文本數據。我們的要做的就是對文本數據進行分類,無監督的分成幾個主要的類別,這里面就牽涉到了一個環節:文本相似度計算。
TF-IDF是最基礎的文本相似度計算方法。TF(Term Frequency)指一篇文檔中單詞出現的頻率,IDF(Inverse Document Frequency)指語料庫中出現某個詞的文檔數,取對數。
TF = 詞在文檔中出現的次數 / 文檔中所有詞的個數
IDF = log(語料庫的文檔總數 / 語料庫中出現某單詞的不同文檔個數)
TF原理:某個詞在一篇文檔中出現的頻率越多則對這篇文章越重要;
IDF原理:該詞在越多的文章中出現,則說明它對文章沒有很強的區分度,在文檔中所占的權重也就越小,一般采用取詞頻的逆。還要考慮一個現象,一些通用詞出現的次數可能是低頻詞的幾十倍上百倍,如果只是簡單的取逆處理,通用詞的權重會變動非常小,稀缺詞的權重就顯得的過大了。為了平衡通用詞與稀缺詞的權重關系,又對逆采用取對數運算。
TF-IDF算法的優勢在於算法簡單,並且對文章的所有元素進行了綜合考量。但也存在致命的不足,TF-IDF把文章的每個詞看做獨立的個體進行處理,忽略了詞的意義,詞之間的關聯關系等因素,在這方面Word2Vector的算法就做的很好。
TF-IDF實踐步驟,也即是一般的文本處理和模型訓練步驟:
1.獲取原始文本內容信息。
2.轉換成純小寫,按空格把文章分成獨立的詞組成的list。
3.去除噪音符號: ["\"","=","\\","/",":","-","(",")",",",".","\n"]等
4.去除停用詞
5.提取詞干,把相近的詞轉換為標准形式,比如把文章中的go,going,went,goes統一成go
6.wordcount,統計每個詞出現的次數,去掉出現次數較少的詞,比如在一百篇文檔中,只出現了1~2次的詞,顯然是沒有意義的。
7.訓練idf模型
8.對輸入的每篇測試文章計算其tfidf向量,然后可以利用tfidf向量求文章之間的相似度(比如用歐拉距離,余弦相似度,Jaccard系數等方法)。
代碼實現,采用spark MLlib提供的TF-IDF庫。
def splitSeq2Words(notes): wordlist = notes.lower().split(" ") return wordlist def removeNoiseChars(word): for char in noiseChars: if char in word: word = word.replace(char, "") return word def dataCleanProcessing(content): content = splitSeq2Words(content) rawWordData1 = map(lambda word:removeNoiseChars(word),content) rawWordData2 = filter(lambda word:word != '', rawWordData1) rawWordData3 = filter(lambda word:not StopWordSet.__contains__(word),rawWordData2) rawWordData4 = filter(lambda word:not OnceWordSet.__contains__(word),rawWordData3) return rawWordData4 summaryContentList = translate_training_sample("./data/trainingSample.xml") summaryContentRDD = sc.parallelize(summaryContentList) cleanData = summaryContentRDD.map(lambda content:dataCleanProcessing(content)) cleanData.count() cleanData.cache() print cleanData.take(10) tf = HashingTF(numFeatures = 100) tfVectorMatrix = tf.transform(cleanData) tfVectorMatrix.cache() print tfVectorMatrix.count() print tfVectorMatrix.take(10) idf = IDF() idfModel = idf.fit(tfVectorMatrix)