Java實(shí)現(xiàn)TFIDF算法代碼分享
算法介紹
概念
TF-IDF(term frequency–inverse document frequency)是一種用于資訊檢索與資訊探勘的常用加權(quán)技術(shù)。TF-IDF是一種統(tǒng)計(jì)方法,用以評(píng)估一字詞對(duì)于一個(gè)文件集或一個(gè)語(yǔ)料庫(kù)中的其中一份文件的重要程度。字詞的重要性隨著它在文件中出現(xiàn)的次數(shù)成正比增加,但同時(shí)會(huì)隨著它在語(yǔ)料庫(kù)中出現(xiàn)的頻率成反比下降。TF-IDF加權(quán)的各種形式常被搜尋引擎應(yīng)用,作為文件與用戶查詢之間相關(guān)程度的度量或評(píng)級(jí)。除了TF-IDF以外,因特網(wǎng)上的搜尋引擎還會(huì)使用基于連結(jié)分析的評(píng)級(jí)方法,以確定文件在搜尋結(jié)果中出現(xiàn)的順序。
原理
在一份給定的文件里,詞頻(termfrequency,TF)指的是某一個(gè)給定的詞語(yǔ)在該文件中出現(xiàn)的次數(shù)。這個(gè)數(shù)字通常會(huì)被歸一化(分子一般小于分母區(qū)別于IDF),以防止它偏向長(zhǎng)的文件。(同一個(gè)詞語(yǔ)在長(zhǎng)文件里可能會(huì)比短文件有更高的詞頻,而不管該詞語(yǔ)重要與否。)
逆向文件頻率(inversedocumentfrequency,IDF)是一個(gè)詞語(yǔ)普遍重要性的度量。某一特定詞語(yǔ)的IDF,可以由總文件數(shù)目除以包含該詞語(yǔ)之文件的數(shù)目,再將得到的商取對(duì)數(shù)得到。
某一特定文件內(nèi)的高詞語(yǔ)頻率,以及該詞語(yǔ)在整個(gè)文件集合中的低文件頻率,可以產(chǎn)生出高權(quán)重的TF-IDF。因此,TF-IDF傾向于過(guò)濾掉常見(jiàn)的詞語(yǔ),保留重要的詞語(yǔ)。
TFIDF的主要思想是:如果某個(gè)詞或短語(yǔ)在一篇文章中出現(xiàn)的頻率TF高,并且在其他文章中很少出現(xiàn),則認(rèn)為此詞或者短語(yǔ)具有很好的類別區(qū)分能力,適合用來(lái)分類。TFIDF實(shí)際上是:TF*IDF,TF詞頻(TermFrequency),IDF反文檔頻率(InverseDocumentFrequency)。TF表示詞條在文檔d中出現(xiàn)的頻率(另一說(shuō):TF詞頻(TermFrequency)指的是某一個(gè)給定的詞語(yǔ)在該文件中出現(xiàn)的次數(shù))。IDF的主要思想是:如果包含詞條t的文檔越少,也就是n越小,IDF越大,則說(shuō)明詞條t具有很好的類別區(qū)分能力。如果某一類文檔C中包含詞條t的文檔數(shù)為m,而其它類包含t的文檔總數(shù)為k,顯然所有包含t的文檔數(shù)n=m+k,當(dāng)m大的時(shí)候,n也大,按照IDF公式得到的IDF的值會(huì)小,就說(shuō)明該詞條t類別區(qū)分能力不強(qiáng)。(另一說(shuō):IDF反文檔頻率(InverseDocumentFrequency)是指果包含詞條的文檔越少,IDF越大,則說(shuō)明詞條具有很好的類別區(qū)分能力。)但是實(shí)際上,如果一個(gè)詞條在一個(gè)類的文檔中頻繁出現(xiàn),則說(shuō)明該詞條能夠很好代表這個(gè)類的文本的特征,這樣的詞條應(yīng)該給它們賦予較高的權(quán)重,并選來(lái)作為該類文本的特征詞以區(qū)別與其它類文檔。這就是IDF的不足之處.
最近要做領(lǐng)域概念的提取,TFIDF作為一個(gè)很經(jīng)典的算法可以作為其中的一步處理。
計(jì)算公式比較簡(jiǎn)單,如下:
預(yù)處理
由于需要處理的候選詞大約后3w+,并且語(yǔ)料文檔數(shù)有1w+,直接挨個(gè)文本遍歷的話很耗時(shí),每個(gè)詞處理時(shí)間都要一分鐘以上。
為了縮短時(shí)間,首先進(jìn)行分詞,一個(gè)詞輸出為一行方便統(tǒng)計(jì),分詞工具選擇的是HanLp。
然后,將一個(gè)領(lǐng)域的文檔合并到一個(gè)文件中,并用“$$$”標(biāo)識(shí)符分割,方便記錄文檔數(shù)。
下面是選擇的領(lǐng)域語(yǔ)料(PATH目錄下):
代碼實(shí)現(xiàn)
package edu.heu.lawsoutput; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @ClassName: TfIdf * @Description: TODO * @author LJH * @date 2017年11月12日 下午3:55:15 */ public class TfIdf { static final String PATH = "E:\\corpus"; // 語(yǔ)料庫(kù)路徑 public static void main(String[] args) throws Exception { String test = "離退休人員"; // 要計(jì)算的候選詞 computeTFIDF(PATH, test); } /** * @param @param path 語(yǔ)料路經(jīng) * @param @param word 候選詞 * @param @throws Exception * @return void */ static void computeTFIDF(String path, String word) throws Exception { File fileDir = new File(path); File[] files = fileDir.listFiles(); // 每個(gè)領(lǐng)域出現(xiàn)候選詞的文檔數(shù) Map<String, Integer> containsKeyMap = new HashMap<>(); // 每個(gè)領(lǐng)域的總文檔數(shù) Map<String, Integer> totalDocMap = new HashMap<>(); // TF = 候選詞出現(xiàn)次數(shù)/總詞數(shù) Map<String, double> tfMap = new HashMap<>(); // scan files for (File f : files) { // 候選詞詞頻 double termFrequency = 0; // 文本總詞數(shù) double totalTerm = 0; // 包含候選詞的文檔數(shù) int containsKeyDoc = 0; // 詞頻文檔計(jì)數(shù) int totalCount = 0; int fileCount = 0; // 標(biāo)記文件中是否出現(xiàn)候選詞 Boolean flag = false; FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String s = ""; // 計(jì)算詞頻和總詞數(shù) while ((s = br.readLine()) != null) { if (s.equals(word)) { termFrequency++; flag = true; } // 文件標(biāo)識(shí)符 if (s.equals("$$$")) { if (flag) { containsKeyDoc++; } fileCount++; flag = false; } totalCount++; } // 減去文件標(biāo)識(shí)符的數(shù)量得到總詞數(shù) totalTerm += totalCount - fileCount; br.close(); // key都為領(lǐng)域的名字 containsKeyMap.put(f.getName(), containsKeyDoc); totalDocMap.put(f.getName(), fileCount); tfMap.put(f.getName(), (double) termFrequency / totalTerm); System.out.println("----------" + f.getName() + "----------"); System.out.println("該領(lǐng)域文檔數(shù):" + fileCount); System.out.println("候選詞出現(xiàn)詞數(shù):" + termFrequency); System.out.println("總詞數(shù):" + totalTerm); System.out.println("出現(xiàn)候選詞文檔總數(shù):" + containsKeyDoc); System.out.println(); } //計(jì)算TF*IDF for (File f : files) { // 其他領(lǐng)域包含候選詞文檔數(shù) int otherContainsKeyDoc = 0; // 其他領(lǐng)域文檔總數(shù) int otherTotalDoc = 0; double idf = 0; double tfidf = 0; System.out.println("~~~~~" + f.getName() + "~~~~~"); Set<Map.Entry<String, Integer>> containsKeyset = containsKeyMap.entrySet(); Set<Map.Entry<String, Integer>> totalDocset = totalDocMap.entrySet(); Set<Map.Entry<String, double>> tfSet = tfMap.entrySet(); // 計(jì)算其他領(lǐng)域包含候選詞文檔數(shù) for (Map.Entry<String, Integer> entry : containsKeyset) { if (!entry.getKey().equals(f.getName())) { otherContainsKeyDoc += entry.getValue(); } } // 計(jì)算其他領(lǐng)域文檔總數(shù) for (Map.Entry<String, Integer> entry : totalDocset) { if (!entry.getKey().equals(f.getName())) { otherTotalDoc += entry.getValue(); } } // 計(jì)算idf idf = log((float) otherTotalDoc / (otherContainsKeyDoc + 1), 2); // 計(jì)算tf*idf并輸出 for (Map.Entry<String, double> entry : tfSet) { if (entry.getKey().equals(f.getName())) { tfidf = (double) entry.getValue() * idf; System.out.println("tfidf:" + tfidf); } } } } static float log(float value, float base) { return (float) (Math.log(value) / Math.log(base)); } }
運(yùn)行結(jié)果
測(cè)試詞為“離退休人員”,中間結(jié)果如下:
最終結(jié)果:
結(jié)論
可以看到“離退休人員”在養(yǎng)老保險(xiǎn)和社保領(lǐng)域,tfidf值比較高,可以作為判斷是否為領(lǐng)域概念的一個(gè)依據(jù)。
當(dāng)然TF-IDF算法雖然很經(jīng)典,但還是有許多不足,不能單獨(dú)依賴其結(jié)果做出判斷。
以上就是本文關(guān)于Java實(shí)現(xiàn)TFIDF算法代碼分享的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
java算法實(shí)現(xiàn)紅黑樹(shù)完整代碼示例
如有不足之處,歡迎留言指出。
相關(guān)文章
spring,mybatis事務(wù)管理配置與@Transactional注解使用詳解
這篇文章主要介紹了spring,mybatis事務(wù)管理配置與@Transactional注解使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07IDEA項(xiàng)目中配置Maven鏡像源(下載源)的詳細(xì)過(guò)程
Maven是一個(gè)能使我們的java程序開(kāi)發(fā)節(jié)省時(shí)間和精力,是開(kāi)發(fā)變得相對(duì)簡(jiǎn)單,還能使開(kāi)發(fā)規(guī)范化的工具,下面這篇文章主要給大家介紹了關(guān)于IDEA項(xiàng)目中配置Maven鏡像源(下載源)的詳細(xì)過(guò)程,需要的朋友可以參考下2024-02-02java之TreeUtils生成一切對(duì)象樹(shù)形結(jié)構(gòu)案例
這篇文章主要介紹了java之TreeUtils生成一切對(duì)象樹(shù)形結(jié)構(gòu)案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Java利用ElasticSearch實(shí)現(xiàn)增刪改功能
這篇文章主要為大家詳細(xì)介紹了Java如何利用ElasticSearch實(shí)現(xiàn)增刪改功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08解決String字符串轉(zhuǎn)JSONObject順序不對(duì)的問(wèn)題
這篇文章主要介紹了解決String字符串轉(zhuǎn)JSONObject順序不對(duì)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12