前阵子做了一些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.去除噪音符号: [""","=","\","/",":","-","(",")",",","."," "]等
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)