算法介紹
最近要做領(lǐng)域概念的提取,TFIDF作為一個(gè)很經(jīng)典的算法可以作為其中的一步處理。
關(guān)于TFIDF算法的介紹可以參考這篇博客【http://www】.ruanyifeng.com/blog/2013/03/tf-idf.html。(請(qǐng)自行把括號(hào)去掉)
計(jì)算公式比較簡(jiǎn)單,如下:
預(yù)處理
由于需要處理的候選詞大約后3w+,并且語料文檔數(shù)有1w+,直接挨個(gè)文本遍歷的話很耗時(shí),每個(gè)詞處理時(shí)間都要一分鐘以上。
為了縮短時(shí)間,首先進(jìn)行分詞,一個(gè)詞輸出為一行方便統(tǒng)計(jì),分詞工具選擇的是HanLp。
然后,將一個(gè)領(lǐng)域的文檔合并到一個(gè)文件中,并用“$$$”標(biāo)識(shí)符分割,方便記錄文檔數(shù)。
下面是選擇的領(lǐng)域語料(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"; // 語料庫路徑
?
????public static void main(String[] args) throws Exception {
?
????????String test = "離退休人員"; // 要計(jì)算的候選詞
?
????????computeTFIDF(PATH, test);
?
????}
?
????/**
????* @param @param path 語料路經(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
????????// 每個(gè)領(lǐng)域的總文檔數(shù)
????????Map
????????// TF = 候選詞出現(xiàn)次數(shù)/總詞數(shù)
????????Map
?
????????// 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
????????????Set
????????????Set
?
????????????// 計(jì)算其他領(lǐng)域包含候選詞文檔數(shù)
????????????for (Map.Entry
????????????????if (!entry.getKey().equals(f.getName())) {
????????????????????otherContainsKeyDoc += entry.getValue();
????????????????}
????????????}
?
????????????// 計(jì)算其他領(lǐng)域文檔總數(shù)
????????????for (Map.Entry
????????????????if (!entry.getKey().equals(f.getName())) {
????????????????????otherTotalDoc += entry.getValue();
????????????????}
????????????}
?
????????????// 計(jì)算idf
????????????idf = log((float) otherTotalDoc / (otherContainsKeyDoc + 1), 2);
?
????????????// 計(jì)算tf*idf并輸出
????????????for (Map.Entry
????????????????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é)果做出判斷。很多論文提出了改進(jìn)方法,本文只是實(shí)現(xiàn)了最基本的算法。如果有其他思路和想法歡迎討論。
?
文章轉(zhuǎn)載自 ??沒課割綠地 的博客
?
評(píng)論