TF-IDF算法解析與Python實(shí)現(xiàn)方法詳解
TF-IDF(term frequency–inverse document frequency)是一種用于信息檢索(information retrieval)與文本挖掘(text mining)的常用加權(quán)技術(shù)。比較容易理解的一個(gè)應(yīng)用場(chǎng)景是當(dāng)我們手頭有一些文章時(shí),我們希望計(jì)算機(jī)能夠自動(dòng)地進(jìn)行關(guān)鍵詞提取。而TF-IDF就是可以幫我們完成這項(xiàng)任務(wù)的一種統(tǒng)計(jì)方法。它能夠用于評(píng)估一個(gè)詞語(yǔ)對(duì)于一個(gè)文集或一個(gè)語(yǔ)料庫(kù)中的其中一份文檔的重要程度。
在一份給定的文件里,詞頻 (term frequency, TF) 指的是某一個(gè)給定的詞語(yǔ)在該文件中出現(xiàn)的次數(shù)。這個(gè)數(shù)字通常會(huì)被歸一化(分子一般小于分母 區(qū)別于IDF),以防止它偏向長(zhǎng)的文件。(同一個(gè)詞語(yǔ)在長(zhǎng)文件里可能會(huì)比短文件有更高的詞頻,而不管該詞語(yǔ)重要與否。)
逆向文件頻率 (inverse document frequency, IDF) 是一個(gè)詞語(yǔ)普遍重要性的度量。某一特定詞語(yǔ)的IDF,可以由總文件數(shù)目除以包含該詞語(yǔ)之文件的數(shù)目,再將得到的商取對(duì)數(shù)得到。
某一特定文件內(nèi)的高詞語(yǔ)頻率,以及該詞語(yǔ)在整個(gè)文件集合中的低文件頻率,可以產(chǎn)生出高權(quán)重的TF-IDF。因此,TF-IDF傾向于過濾掉常見的詞語(yǔ),保留重要的詞語(yǔ)。
TFIDF的主要思想是:如果某個(gè)詞或短語(yǔ)在一篇文章中出現(xiàn)的頻率TF高,并且在其他文章中很少出現(xiàn),則認(rèn)為此詞或者短語(yǔ)具有很好的類別區(qū)分能力,適合用來分類。TFIDF實(shí)際上是:TF * IDF,TF詞頻(Term Frequency),IDF反文檔頻率(Inverse Document Frequency)。TF表示詞條在文檔d中出現(xiàn)的頻率(另一說:TF詞頻(Term Frequency)指的是某一個(gè)給定的詞語(yǔ)在該文件中出現(xiàn)的次數(shù))。IDF的主要思想是:如果包含詞條t的文檔越少,也就是n越小,IDF越大,則說明詞條t具有很好的類別區(qū)分能力。如果某一類文檔C中包含詞條t的文檔數(shù)為m,而其它類包含t的文檔總數(shù)為k,顯然所有包含t的文檔數(shù)n=m+k,當(dāng)m大的時(shí)候,n也大,按照IDF公式得到的IDF的值會(huì)小,就說明該詞條t類別區(qū)分能力不強(qiáng)。(另一說:IDF反文檔頻率(Inverse Document Frequency)是指果包含詞條的文檔越少,IDF越大,則說明詞條具有很好的類別區(qū)分能力。)但是實(shí)際上,如果一個(gè)詞條在一個(gè)類的文檔中頻繁出現(xiàn),則說明該詞條能夠很好代表這個(gè)類的文本的特征,這樣的詞條應(yīng)該給它們賦予較高的權(quán)重,并選來作為該類文本的特征詞以區(qū)別與其它類文檔。這就是IDF的不足之處.
為了演示在Python中實(shí)現(xiàn)TF-IDF的方法,一些基于自然語(yǔ)言處理的預(yù)處理過程也會(huì)在本文中出現(xiàn)。如果你對(duì)NLTK和Scikit-Learn兩個(gè)庫(kù)還很陌生可以參考如下文章:
Python編程使用NLTK進(jìn)行自然語(yǔ)言處理詳解
Python自然語(yǔ)言處理之詞干,詞形與最大匹配算法代碼詳解
必要的預(yù)處理過程
首先,我們給出需要引用的各種包,以及用作處理對(duì)象的三段文本。
import nltk import math import string from nltk.corpus import stopwords from collections import Counter from nltk.stem.porter import * from sklearn.feature_extraction.text import TfidfVectorizer text1 = "Python is a 2000 made-for-TV horror movie directed by Richard \ Clabaugh. The film features several cult favorite actors, including William \ Zabka of The Karate Kid fame, Wil Wheaton, Casper Van Dien, Jenny McCarthy, \ Keith Coogan, Robert Englund (best known for his role as Freddy Krueger in the \ A Nightmare on Elm Street series of films), Dana Barron, David Bowe, and Sean \ Whalen. The film concerns a genetically engineered snake, a python, that \ escapes and unleashes itself on a small town. It includes the classic final\ girl scenario evident in films like Friday the 13th. It was filmed in Los Angeles, \ California and Malibu, California. Python was followed by two sequels: Python \ II (2002) and Boa vs. Python (2004), both also made-for-TV films." text2 = "Python, from the Greek word (πύθων/πύθωνας), is a genus of \ nonvenomous pythons[2] found in Africa and Asia. Currently, 7 species are \ recognised.[2] A member of this genus, P. reticulatus, is among the longest \ snakes known." text3 = "The Colt Python is a .357 Magnum caliber revolver formerly \ manufactured by Colt's Manufacturing Company of Hartford, Connecticut. \ It is sometimes referred to as a \"Combat Magnum\".[1] It was first introduced \ in 1955, the same year as Smith & Wesson's M29 .44 Magnum. The now discontinued \ Colt Python targeted the premium revolver market segment. Some firearm \ collectors and writers such as Jeff Cooper, Ian V. Hogg, Chuck Hawks, Leroy \ Thompson, Renee Smeets and Martin Dougherty have described the Python as the \ finest production revolver ever made."
TF-IDF的基本思想是:詞語(yǔ)的重要性與它在文件中出現(xiàn)的次數(shù)成正比,但同時(shí)會(huì)隨著它在語(yǔ)料庫(kù)中出現(xiàn)的頻率成反比下降。 但無論如何,統(tǒng)計(jì)每個(gè)單詞在文檔中出現(xiàn)的次數(shù)是必要的操作。所以說,TF-IDF也是一種基于 bag-of-word 的方法。
首先我們來做分詞,其中比較值得注意的地方是我們?cè)O(shè)法剔除了其中的標(biāo)點(diǎn)符號(hào)(顯然,標(biāo)點(diǎn)符號(hào)不應(yīng)該成為最終的關(guān)鍵詞)。
def get_tokens(text): lowers = text.lower() #remove the punctuation using the character deletion step of translate remove_punctuation_map = dict((ord(char), None) for char in string.punctuation) no_punctuation = lowers.translate(remove_punctuation_map) tokens = nltk.word_tokenize(no_punctuation) return tokens
下面的代碼用于測(cè)試上述分詞結(jié)果,Counter() 函數(shù)用于統(tǒng)計(jì)每個(gè)單詞出現(xiàn)的次數(shù)。
tokens = get_tokens(text1) count = Counter(tokens) print (count.most_common(10))
執(zhí)行上述代碼后可以得到如下結(jié)果,我們輸出了其中出現(xiàn)次數(shù)最多的10個(gè)詞。
[('the', 6), ('python', 5), ('a', 5), ('and', 4), ('films', 3), ('in', 3), ('madefortv', 2), ('on', 2), ('by', 2), ('was', 2)]
顯然,像 the, a, and 這些詞盡管出現(xiàn)的次數(shù)很多,但是它們與文檔所表述的主題是無關(guān)的,所以我們還要去除“詞袋”中的“停詞”(stop words),代碼如下:
def stem_tokens(tokens, stemmer): stemmed = [] for item in tokens: stemmed.append(stemmer.stem(item)) return stemmed
同樣,我們來測(cè)試一下上述代碼的執(zhí)行效果。
tokens = get_tokens(text1) filtered = [w for w in tokens if not w in stopwords.words('english')] count = Counter(filtered) print (count.most_common(10))
從下面的輸出結(jié)果你會(huì)發(fā)現(xiàn),之前那些缺乏實(shí)際意義的 the, a, and 等詞已經(jīng)被過濾掉了。
[('python', 5), ('films', 3), ('film', 2), ('california', 2), ('madefortv', 2), ('genetically', 1), ('horror', 1), ('krueger', 1), ('filmed', 1), ('sean', 1)]
但這個(gè)結(jié)果還是不太理想,像 films, film, filmed 其實(shí)都可以看出是 film,而不應(yīng)該把每個(gè)詞型都分別進(jìn)行統(tǒng)計(jì)。這時(shí)就需要要用到我們?cè)谇懊嫖恼轮性?jīng)介紹過的 Stemming 方法。代碼如下:
tokens = get_tokens(text1) filtered = [w for w in tokens if not w in stopwords.words('english')] stemmer = PorterStemmer() stemmed = stem_tokens(filtered, stemmer)
類似地,我們輸出計(jì)數(shù)排在前10的詞匯(以及它們出現(xiàn)的次數(shù)):
count = Counter(stemmed) print(count)
上述代碼執(zhí)行結(jié)果如下:
Counter({'film': 6, 'python': 5, 'madefortv': 2, 'california': 2, 'includ': 2, '2004': 1, 'role': 1, 'casper': 1, 'robert': 1, 'sequel': 1, 'two': 1, 'krueger': 1, 'ii': 1, 'sean': 1, 'lo': 1, 'clabaugh': 1, 'finalgirl': 1, 'wheaton': 1, 'concern': 1, 'whalen': 1, 'cult': 1, 'boa': 1, 'mccarthi': 1, 'englund': 1, 'best': 1, 'direct': 1, 'known': 1, 'favorit': 1, 'movi': 1, 'keith': 1, 'karat': 1, 'small': 1, 'classic': 1, 'coogan': 1, 'like': 1, 'elm': 1, 'fame': 1, 'malibu': 1, 'sever': 1, 'richard': 1, 'scenario': 1, 'town': 1, 'friday': 1, 'david': 1, 'unleash': 1, 'vs': 1, '2000': 1, 'angel': 1, 'nightmar': 1, 'zabka': 1, '13th': 1, 'jenni': 1, 'seri': 1, 'horror': 1, 'william': 1, 'street': 1, 'wil': 1, 'escap': 1, 'van': 1, 'snake': 1, 'evid': 1, 'freddi': 1, 'bow': 1, 'dien': 1, 'follow': 1, 'engin': 1, 'also': 1})
至此,我們就完成了基本的預(yù)處理過程。
TF-IDF的算法原理
預(yù)處理過程中,我們已經(jīng)把停詞都過濾掉了。如果只考慮剩下的有實(shí)際意義的詞,前我們已經(jīng)講過,顯然詞頻(TF,Term Frequency)較高的詞之于一篇文章來說可能是更為重要的詞(也就是潛在的關(guān)鍵詞)。但這樣又會(huì)遇到了另一個(gè)問題,我們可能發(fā)現(xiàn)在上面例子中,madefortv、california、includ 都出現(xiàn)了2次(madefortv其實(shí)是原文中的made-for-TV,因?yàn)槲覀兯x分詞法的緣故,它被當(dāng)做是一個(gè)詞來看待),但這顯然并不意味著“作為關(guān)鍵詞,它們的重要性是等同的”。
因?yàn)椤眎nclud”是很常見的詞(注意 includ 是 include 的詞干)。相比之下,california 可能并不那么常見。如果這兩個(gè)詞在一篇文章的出現(xiàn)次數(shù)一樣多,我們有理由認(rèn)為,california 重要程度要大于 include ,也就是說,在關(guān)鍵詞排序上面,california 應(yīng)該排在 include 的前面。
于是,我們需要一個(gè)重要性權(quán)值調(diào)整參數(shù),來衡量一個(gè)詞是不是常見詞。如果某個(gè)詞比較少見,但是它在某篇文章中多次出現(xiàn),那么它很可能就反映了這篇文章的特性,它就更有可能揭示這篇文字的話題所在。這個(gè)權(quán)重調(diào)整參數(shù)就是“逆文檔頻率”(IDF,Inverse Document Frequency),它的大小與一個(gè)詞的常見程度成反比。
知道了 TF 和 IDF 以后,將這兩個(gè)值相乘,就得到了一個(gè)詞的TF-IDF值。某個(gè)詞對(duì)文章的重要性越高,它的TF-IDF值就越大。如果用公式來表示,則對(duì)于某個(gè)特定文件中的詞語(yǔ) ti 而言,它的 TF 可以表示為:
其中 ni,j是該詞在文件 dj中出現(xiàn)的次數(shù),而分母則是文件 dj 中所有詞匯出現(xiàn)的次數(shù)總和。如果用更直白的表達(dá)是來描述就是,
某一特定詞語(yǔ)的IDF,可以由總文件數(shù)目除以包含該詞語(yǔ)之文件的數(shù)目,再將得到的商取對(duì)數(shù)即可:
其中,|D| 是語(yǔ)料庫(kù)中的文件總數(shù)。 |{j:ti∈dj}| 表示包含詞語(yǔ) ti 的文件數(shù)目(即 ni,j≠0 的文件數(shù)目)。如果該詞語(yǔ)不在語(yǔ)料庫(kù)中,就會(huì)導(dǎo)致分母為零,因此一般情況下使用 1+|{j:ti∈dj}|。同樣,如果用更直白的語(yǔ)言表示就是
最后,便可以來計(jì)算 TF-IDF(t)=TF(t)×IDF(t)。
下面的代碼實(shí)現(xiàn)了計(jì)算TF-IDF值的功能。
def tf(word, count): return count[word] / sum(count.values()) def n_containing(word, count_list): return sum(1 for count in count_list if word in count) def idf(word, count_list): return math.log(len(count_list) / (1 + n_containing(word, count_list))) def tfidf(word, count, count_list): return tf(word, count) * idf(word, count_list)
再給出一段測(cè)試代碼:
countlist = [count1, count2, count3] for i, count in enumerate(countlist): print("Top words in document {}".format(i + 1)) scores = {word: tfidf(word, count, countlist) for word in count} sorted_words = sorted(scores.items(), key=lambda x: x[1], reverse=True) for word, score in sorted_words[:3]: print("\tWord: {}, TF-IDF: {}".format(word, round(score, 5)))
輸出結(jié)果如下:
Top words in document 1 Word: film, TF-IDF: 0.02829 Word: madefortv, TF-IDF: 0.00943 Word: california, TF-IDF: 0.00943 Top words in document 2 Word: genu, TF-IDF: 0.03686 Word: 7, TF-IDF: 0.01843 Word: among, TF-IDF: 0.01843 Top words in document 3 Word: revolv, TF-IDF: 0.02097 Word: colt, TF-IDF: 0.02097 Word: manufactur, TF-IDF: 0.01398
利用Scikit-Learn實(shí)現(xiàn)的TF-IDF
因?yàn)?TF-IDF 在文本數(shù)據(jù)挖掘時(shí)十分常用,所以在Python的機(jī)器學(xué)習(xí)包中也提供了內(nèi)置的TF-IDF實(shí)現(xiàn)。主要使用的函數(shù)就是TfidfVectorizer(),來看一個(gè)簡(jiǎn)單的例子。
>>> corpus = ['This is the first document.', 'This is the second second document.', 'And the third one.', 'Is this the first document?',] >>> vectorizer = TfidfVectorizer(min_df=1) >>> vectorizer.fit_transform(corpus) <4x9 sparse matrix of type '<class 'numpy.float64'>' with 19 stored elements in Compressed Sparse Row format> >>> vectorizer.get_feature_names() ['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this'] >>> vectorizer.fit_transform(corpus).toarray() array([[ 0. , 0.43877674, 0.54197657, 0.43877674, 0. , 0. , 0.35872874, 0. , 0.43877674], [ 0. , 0.27230147, 0. , 0.27230147, 0. , 0.85322574, 0.22262429, 0. , 0.27230147], [ 0.55280532, 0. , 0. , 0. , 0.55280532, 0. , 0.28847675, 0.55280532, 0. ], [ 0. , 0.43877674, 0.54197657, 0.43877674, 0. , 0. , 0.35872874, 0. , 0.43877674]])
最終的結(jié)果是一個(gè) 4×9 矩陣。每行表示一個(gè)文檔,每列表示該文檔中的每個(gè)詞的評(píng)分。如果某個(gè)詞沒有出現(xiàn)在該文檔中,則相應(yīng)位置就為 0 。數(shù)字 9 表示語(yǔ)料庫(kù)里詞匯表中一共有 9 個(gè)(不同的)詞。例如,你可以看到在文檔1中,并沒有出現(xiàn) and,所以矩陣第一行第一列的值為 0 。單詞 first 只在文檔1中出現(xiàn)過,所以第一行中 first 這個(gè)詞的權(quán)重較高。而 document 和 this 在 3 個(gè)文檔中出現(xiàn)過,所以它們的權(quán)重較低。而 the 在 4 個(gè)文檔中出現(xiàn)過,所以它的權(quán)重最低。
最后需要說明的是,由于函數(shù) TfidfVectorizer() 有很多參數(shù),我們這里僅僅采用了默認(rèn)的形式,所以輸出的結(jié)果可能與采用前面介紹的(最基本最原始的)算法所得出之結(jié)果有所差異(但數(shù)量的大小關(guān)系并不會(huì)改變)。有興趣的讀者可以參考這里來了解更多關(guān)于在Scikit-Learn中執(zhí)行 TF-IDF 算法的細(xì)節(jié)。
總結(jié)
以上就是本文關(guān)于TF-IDF算法解析與Python實(shí)現(xiàn)方法詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
Python自然語(yǔ)言處理之詞干,詞形與最大匹配算法代碼詳解
Python算法輸出1-9數(shù)組形成的結(jié)果為100的所有運(yùn)算式
如有不足之處,歡迎留言指出。
記者問一個(gè)大爺:大爺,您保持亮麗的秘訣是什么?
大爺說:白天敲代碼,晚上擼系統(tǒng),姿勢(shì)不要?jiǎng)?,眼?dòng)手動(dòng)就可以。
記者:???大爺您是做什么工作的?
大爺:敲代碼的呀。
記者:那大爺您是本身就很喜歡光頭的嗎?
大爺:掉光的~
相關(guān)文章
Python辦公自動(dòng)化之?dāng)?shù)據(jù)可視化與報(bào)表生成
在現(xiàn)代辦公環(huán)境中,數(shù)據(jù)處理和報(bào)表生成是一項(xiàng)重要的任務(wù),本文將高效介紹如何使用Python進(jìn)行數(shù)據(jù)可視化和報(bào)表生成,讓您的辦公工作更加順利2023-07-07使用python將excel數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)過程詳解
這篇文章主要介紹了使用python將excel數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Python中random模塊生成隨機(jī)數(shù)詳解
本文給大家匯總了一下在Python中random模塊中最常用的生成隨機(jī)數(shù)的方法,有需要的小伙伴可以參考下2016-03-03python函數(shù)指定默認(rèn)值的實(shí)例講解
在本篇內(nèi)容里小編給大家整理了一篇關(guān)于python函數(shù)指定默認(rèn)值的實(shí)例講解內(nèi)容,有需要的朋友們可以跟著學(xué)習(xí)參考下。2021-03-03