自然語言處理系列之TF-IDF算法

TF-IDF算法

TF-IDF(詞頻-逆文檔頻率)算法是一種統計方法,用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨着它在文件中出現的次數成正比增加,但同時會隨着它在語料庫中出現的頻率成反比下降。該算法在數據挖掘、文本處理和信息檢索等領域得到了廣泛的應用,如從一篇文章中找到它的關鍵詞。

TFIDF的主要思想是:如果某個詞或短語在一篇文章中出現的頻率TF高,並且在其他文章中很少出現,則認爲此詞或者短語具有很好的類別區分能力,適合用來分類。TF-IDF實際上就是 TF*IDF,其中 TF(Term Frequency),表示詞條在文章Document 中出現的頻率;IDF(Inverse Document Frequency),其主要思想就是,如果包含某個詞 Word的文檔越少,則這個詞的區分度就越大,也就是 IDF 越大。對於如何獲取一篇文章的關鍵詞,我們可以計算這邊文章出現的所有名詞的 TF-IDF,TF-IDF越大,則說明這個名詞對這篇文章的區分度就越高,取 TF-IDF 值較大的幾個詞,就可以當做這篇文章的關鍵詞。

計算步驟

  1. 計算詞頻(TF)

    =

  2. 計算逆文檔頻率(IDF)

    =log+1

  3. 計算詞頻-逆文檔頻率(TF-IDF)
    =

代碼示例

爲了建立自己的語料庫,我從網上爬了100個 url 存放在本地的 txt 文件中,
存放格式如下:
這裏寫圖片描述
這就相當於存放了100篇文章,即語料庫的的文檔總數是100。

  • TF-IDF 算法代碼
package com.myapp.ml.nlp;

import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.ansj.util.FilterModifWord;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by lionel on 16/12/15.
 */
public class TFIDFAlgorithm {
    /**
     * 根據文件路徑,文件中存放的100個網址的 url,獲取 url 路徑列表
     *
     * @param path 本地文件路徑
     * @return 路徑列表
     */
    public List<String> readUrlFromText(String path) {
        if (StringUtils.isBlank(path)) {
            return null;
        }
        List<String> urls = new ArrayList<String>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(path));
            String line;
            while ((line = reader.readLine()) != null) {
                urls.add(line.trim());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return urls;
    }

    /**
     * 利用 Jsoup 工具,根據網址獲取網頁文本
     *
     * @param url 網址
     * @return 網頁文本
     */
    public String getTextFromUrl(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }

        String text = "";
        try {
            Document document = Jsoup.connect(url).get();
            text = document.text();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return text.replace(" ", "");
    }

    /**
     * 運用 ansj 給文本分詞
     *
     * @param text 文本內容
     * @return 分詞結果
     */
    public List<Term> parse(String text) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        List<Term> terms = FilterModifWord.modifResult(ToAnalysis.parse(text));
        if (terms == null || terms.size() == 0) {
            return null;
        }
        return terms;
    }

    /**
     * 計算一篇文章分詞後除去標點符號後詞的總數
     *
     * @param terms 分詞後的集合
     * @return 一篇文章分詞後除去標點符號後詞的總數
     */
    private Integer countWord(List<Term> terms) {
        if (terms == null || terms.size() == 0) {
            return null;
        }
        for (int i = 0; i < terms.size(); i++) {
            if ("null".equals(terms.get(i).getNatureStr()) || terms.get(i).getNatureStr().startsWith("w")) {
                terms.remove(i);
            }
        }
        return terms.size();
    }

    /**
     * 計算詞頻 IF
     *
     * @param word  詞
     * @param terms 分詞結果集合
     * @return IF
     */
    public double computeTF(String word, List<Term> terms) {
        if (StringUtils.isBlank(word)) {
            return 0.0;
        }
        int count = 0;
        for (Term term : terms) {
            if (term.getName().equals(word)) {
                count += 1;
            }
        }
        return (double) count / countWord(terms);
    }

    /**
     * 統計詞語的逆文檔頻率 IDF
     *
     * @param path 存放 url 的文件路徑
     * @param word IDF
     */
    public double computeIDF(String path, String word) {
        if (StringUtils.isBlank(path) || StringUtils.isBlank(word)) {
            return 0.0;
        }

        List<String> urls = readUrlFromText(path);
        int count = 1;
        for (String url : urls) {
            String text = getTextFromUrl(url);
            if (text.contains(word)) {
                count += 1;
            }
        }
        return Math.log10((double) urls.size() / count);
    }

    /**
     * 計算詞頻-逆文檔頻率 TF—IDF
     *
     * @param filePath 存放url的文件路徑
     * @param terms    分詞結果集合
     * @param word     詞
     * @return TF—IDF
     */

    public Double computeTFIDF(String filePath, List<Term> terms, String word) {
        return computeTF(word, terms) * computeIDF(filePath, word);
    }
}
  • 測試代碼
package com.myapp.ml.nlp;

import org.ansj.domain.Term;
import org.junit.Test;

import java.util.List;

/**
 *測試詞語“語言”的 TF-IDF
 * Created by lionel on 16/12/15.
 */
public class TFIDFAlgorithmTest {
    @Test
    public void test() {
        TFIDFAlgorithm tfidfAlgorithm = new TFIDFAlgorithm();
        String filePath = "/Users/lionel/PycharmProjects/python-app/com/pythonapp/spider/output.txt";
        String url = "http://baike.baidu.com/item/Java/85979";
        String word = "語言";
        List<Term> terms = tfidfAlgorithm.parse(tfidfAlgorithm.getTextFromUrl(url));
        System.out.println("[【" + word + "】詞頻 ] " + tfidfAlgorithm.computeTF(word, terms));
        System.out.println("[【" + word + "】逆文檔頻率 ] " + tfidfAlgorithm.computeIDF(filePath, word));
        System.out.println("[【" + word + "】詞頻-逆文檔頻率 ] "+tfidfAlgorithm.computeTFIDF(filePath,terms,word));

    }
}
  • 測試結果
    這裏寫圖片描述

本篇博客描述的是我對 TF-IDF 算法的理解,代碼是對算法過程簡單的實現,若有失偏頗,還請指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章