Term Frequency (TF) e Term Frequency – Inverse Document Frequency (TF-IDF)

Term Frequency (TF) e Term Frequency – Inverse Document Frequency (TF-IDF)

É bastante comum quando utilizamos o Processamento de Linguagem Natural (PLN) termos de escolher quais são as abordagens que iremos utilizar. Atualmente existem três caminhos disponíveis. O primeiro deles é utilizar algoritmos estatísticos; O segundo é a utilização de algoritmos linguísticos. Por fim, podemos buscar aproveitar o melhor de dois mundos e utilizar abordagens híbridas.   

Este tema já foi previamente discutido neste post. Vale a pena ler, caso você ainda não está familiarizado com este assunto.   A abordagem utilizada no algoritmo de Term Frequency (TF) e Term Frequency – Inverse Document Frequency (TF-IDF) é estatística.

De forma geral os dois algoritmos estão conectados pois o TF-IDF é uma extensão do TF.   

Term Frequency  

A tradução do nome deste algoritmo já nos dá uma dica do que ele trata. A “frequência dos termos” é o processo de encontrar quantas vezes um termo foi repetido em uma sentença. 

Veja um exemplo bastante simples:  

Linda é linda.

Nesta sentença podemos afirmar que:  

  • – Existem 3 de termos nesta sentença;
  •  – A frequência da palavra “Linda” é de 2/3;
  •  – A frequência da palavra “é” é de 1/3;    

Veja este algoritmo implementado na linguagem JAVA:



import Model.Word;
import Util.StringUtils;
import java.util.ArrayList;
import java.util.LinkedHashSet;

public class TermFrequency {
    //Gera um ArrayList de palavras
    public ArrayList<Word> getTermFrequency(String textToParse) {
        ArrayList<String> words =            
                         stringToArrayOfWords(textToParse);
        ArrayList<Word> retorno = new ArrayList<>();
        
        //Popula o array
        for (String s : words) {
            double count = 0;
            for (String i : words) {
                if (s.equals(i)) {
                    count++;
                }
            }
            if (!s.isEmpty()) {
                double a = count/words.size();
                retorno.add(new Word(s,count/words.size()));
            }
        }

        //Remove palavras repetidas usando HashSet

        LinkedHashSet<Word> temp = new LinkedHashSet<>();
        temp.addAll(retorno);
        retorno.clear();
        retorno.addAll(temp);
        return retorno;
    }

 //AUXILIAR METHODS
    public static ArrayList<String> stringToArrayOfWords(String contentToConvert) {
        ArrayList<String> words = new ArrayList<>();
        if (contentToConvert != null) {
            for (String s : contentToConvert.split(" ")) {
                words.add(s);
            }
        }
        return words;

    }

}

//Classe palavras

public class Word{

    private String word;
    private double frequency;

    public Word(String word, double frequency) {
        this.word = word;
        this.frequency = frequency;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public double getFrequency() {
        return frequency;
    }

    public void setFrequency(double frequency) {
        this.frequency = frequency;

    }
}

Term Frequency – Inverse Document Frequency    

Este algoritmo utiliza o mesmo conceito de frequência de termos, porém ele trabalha com uma adaptação visando melhorar o seu desempenho. Esta modificação foi pensada para tratar as palavras existentes no corpus analisado que não significam nada para o conjunto. Veja o exemplo abaixo:  

  • Documento 1:  Eu sou homem.
  • Documento 2:  Eu sou médico.
  • Documento 3: Como um bom médico, eu fiz vários cursos de especialização.  

Se utilizarmos o algoritmo de TF (sem pré-processamento) podemos perceber que é possível que uma palavra ocorra em todos os documentos. Ex: “Eu”. Ao contrário disso também é possível, ou seja, é perfeitamente possível que uma palavra só ocorra em um documento. Ex: “fiz”.   

Ao analisar estes fatos é possível concluir que estes dois extremos são ruins para classificação de documentos. Se uma palavra ocorre em todos eles, não é possível dizer que aquela palavra é um diferencial. O mesmo podemos dizer se a palavra ocorre pouquíssimas vezes.   Para isso o algoritmo utiliza uma razão inversa para calcular a frequência destes termos. Observe o algoritmo:   TF * IDF

TF(t) = (Número de vezes que t aparece no documento) / (Número total de termos no documento).

IDF(t) = log_e(Número total de termos do documento/ Número de documentos que possuem o termo t).


Desta forma, tendem a se aproximar de zero, todos os termos que se repetem em todos os documentos ou aqueles que aparecem em apenas um documento.


Veja o algoritmo implementado em JAVA:

import Model.Word;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

public class TermFrequencyIdf {

   public ArrayList<Word> 
                    getTermFrequencyIdf(
                          ArrayList<ArrayList<String>> docs, 
                          String textToParse) {
           ArrayList<String> wordsToParse =                    
                                stringToArrayOfWords(textToParse);
        ArrayList<Word> retorno = new ArrayList<>();

        //Contabiliza os termos
        for (String s : wordsToParse) {

            double count = 0;
            retorno.add(new Word(s,tfIdf(wordsToParse, docs, s)));
        }


       //Remove termos repetidos
        LinkedHashSet<Word> temp = new LinkedHashSet<>();
        temp.addAll(retorno);
        retorno.clear();
        retorno.addAll(temp);
        return retorno;
    }

    public double tf(ArrayList<String> doc, String term) {
        double result = 0;
        for (String word : doc) {
            if (term.equalsIgnoreCase(word)) {
                result++;
            }
        }
        return result / doc.size();
    }
    //Faz a contagem de acordo com a relação inversa dos documentos
    public double idf(ArrayList<ArrayList<String>> docs, String term) {
        double n = 0;
        for (List<String> doc : docs) {
            for (String word : doc) {
                if (term.equalsIgnoreCase(word)) {
                    n++;
                    break;
                }
            }
        }

        return Math.log(docs.size() / n);
    }


   //Retorna o valor do TF-IDF
    public double tfIdf(
                ArrayList<String> doc, 
                ArrayList<ArrayList<String>> docs, 
                String term) {
        return tf(doc, term) * idf(docs, term);
    }
}

Estes dois algoritmos são muito interessantes, porém é importante que para utiliza-los você tenha estudado um pouco de Pre-processamento. Visto que existem muitas palavras que são consideradas “lixo” por serem não significativas. Estes ruídos podem prejudicar a aplicação dos algoritmos descritos acima.  

Vinicius dos Santos

Apenas um apaixonado por Ciência da Computação e forma com que ela pode transformar vidas!

Deixe uma resposta