算法介紹html
最近要作領域概念的提取,TFIDF做爲一個很經典的算法能夠做爲其中的一步處理。java
關於TFIDF算法的介紹能夠參考這篇博客http://www.ruanyifeng.com/blog/2013/03/tf-idf.html。c++
計算公式比較簡單,以下:算法
預處理工具
因爲須要處理的候選詞大約後3w+,而且語料文檔數有1w+,直接挨個文本遍歷的話很耗時,每一個詞處理時間都要一分鐘以上。測試
爲了縮短期,首先進行分詞,一個詞輸出爲一行方便統計,分詞工具選擇的是HanLp。spa
而後,將一個領域的文檔合併到一個文件中,並用「$$$」標識符分割,方便記錄文檔數。.net
下面是選擇的領域語料(PATH目錄下):htm
代碼實現blog
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 = "離退休人員"; // 要計算的候選詞
computeTFIDF(PATH, test);
}
/**
* @param @param path 語料路經
* @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();
// 每一個領域出現候選詞的文檔數
Map<String, Integer> containsKeyMap = new HashMap<>();
// 每一個領域的總文檔數
Map<String, Integer> totalDocMap = new HashMap<>();
// TF = 候選詞出現次數/總詞數
Map<String, Double> tfMap = new HashMap<>();
// scan files
for (File f : files) {
// 候選詞詞頻
double termFrequency = 0;
// 文本總詞數
double totalTerm = 0;
// 包含候選詞的文檔數
int containsKeyDoc = 0;
// 詞頻文檔計數
int totalCount = 0;
int fileCount = 0;
// 標記文件中是否出現候選詞
boolean flag = false;
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String s = "";
// 計算詞頻和總詞數
while ((s = br.readLine()) != null) {
if (s.equals(word)) {
termFrequency++;
flag = true;
}
// 文件標識符
if (s.equals("$$$")) {
if (flag) {
containsKeyDoc++;
}
fileCount++;
flag = false;
}
totalCount++;
}
// 減去文件標識符的數量獲得總詞數
totalTerm += totalCount - fileCount;
br.close();
// key都爲領域的名字
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("該領域文檔數:" + fileCount);
System.out.println("候選詞出現詞數:" + termFrequency);
System.out.println("總詞數:" + totalTerm);
System.out.println("出現候選詞文檔總數:" + containsKeyDoc);
System.out.println();
}
//計算TF*IDF
for (File f : files) {
// 其餘領域包含候選詞文檔數
int otherContainsKeyDoc = 0;
// 其餘領域文檔總數
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();
// 計算其餘領域包含候選詞文檔數
for (Map.Entry<String, Integer> entry : containsKeyset) {
if (!entry.getKey().equals(f.getName())) {
otherContainsKeyDoc += entry.getValue();
}
}
// 計算其餘領域文檔總數
for (Map.Entry<String, Integer> entry : totalDocset) {
if (!entry.getKey().equals(f.getName())) {
otherTotalDoc += entry.getValue();
}
}
// 計算idf
idf = log((float) otherTotalDoc / (otherContainsKeyDoc + 1), 2);
// 計算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));
}
}
運行結果
測試詞爲「離退休人員」,中間結果以下:
最終結果:
結論
能夠看到「離退休人員」在養老保險和社保領域,tfidf值比較高,能夠做爲判斷是否爲領域概念的一個依據。固然TF-IDF算法雖然很經典,但仍是有許多不足,不能單獨依賴其結果作出判斷。不少論文提出了改進方法,本文只是實現了最基本的算法。若是有其餘思路和想法歡迎討論。
文章轉載自 沒課割綠地 的博客