Como criar um saco de palavras em Python (bag of words)

Como criar um saco de palavras em Python (bag of words)

Nessa aula vamos entender o que é um “saco de palavras” e como implementar um bag of words em python. Essa técnica é amplamente utilizada dentro do processamento de linguagem natural principalmente para criação de ferramentas como detectores de Span ou de sentimentos. Mas como isso acontece? como podemos criar esse “saco de palavras” usando o python? Veja a seguir.

O que é um “Saco de palavras” (bag of words)?

O algoritmo de bag-of-words é muito conhecido e já foi amplamente discutido e implementado por outros desenvolvedores. O modelo bag-of-words é uma representação simplificadora usada no processamento de linguagem natural. Nesse modelo, um texto (como uma frase ou um documento) é representado como a bolsa de suas palavras, desconsiderando a gramática e até mesmo a ordem das palavras, mas mantendo a multiplicidade.

Vamos imaginar o seguinte exemplo, temos um documento que possui apenas uma frase:

"Subi na escada do Rio de Janeiro, aquela escada toda colorida"

Para criar um “saco de palavras” é preciso quebrar cada uma das palavras:

Subi
na
escada
do
Rio
de
Janeiro
aquela
escada
toda
colorida

Após essa quebra, podemos agrupar as palavras e listar a frequência:

Subi → 1
na → 1
escada → 2
do → 1
Rio → 1
de → 1
Janeiro → 1
aquela → 1
toda → 1
colorida → 1

Podemos perceber que nessa sentença quase tudo apareceu apenas 1 vez, apenas a palavra “escada” aparece 2 vezes. Não é por acaso também, que essa sentença possui como “tema principal” a escada do Rio de Janeiro. Essa frequência de palavras muitas vezes é usada para indicar que aquela palavra possui grande importância para o texto.

Texto adaptado daqui

Usando o Python para criar um saco de palavras

Para criar um algoritmo simples de bag of words em python podemos usar uma função e manipular as strings. Porém, existem diversas tarefas consideradas “intermediárias” que também fazem parte desse processo. Por exemplo, para realizar a separação de cada palavra da frase em um elemento de um vetor, temos o algoritmo chamado de “Tokenize”. Em teoria ele tem um funcionamento muito simples, basta “quebrar” a sentença baseado nos espaços, no entanto, alguns problemas podem aparecer quando queremos quebrar essa sentença quando temos pontuação.

Veja um exemplo simples:

A maria, aquela da escola, possui lindas bonecas. 

A tokenização dessa sentença, se fosse feita de forma “amadora” teria o seguinte resultado:

{"A": 1', "maria,": 1, "aquela": 1, "da": 1, "escola,": 1, "possui": 1, "lindas": 1, "bonecas.": 1}

Por esse motivo e por outros costumamos usar o NLTK ou outros frameworks de processamento de linguagem natural para facilitar nosso trabalho e evitar erros assim. Veja um exemplo de como tokenizar uma frase usando o NLTK:

from nltk.tokenize import word_tokenize
text = word_tokenize("Eu amo muito bacon!")
print(text)

Output: ['eu', 'amo', 'muito', 'bacon', '!']

Perceba que essa tokenização não eliminou a pontuação, simplesmente separou ela em um único token. Porém, a partir disso você pode simplesmente excluir tokens que são apenas pontos ou símbolos.

Agora que já entendemos a tokenização e como ela acontece em python, podemos pensar em como criar o nosso saco de palavras usando o NLTK. Para isso vamos usar a ferramenta Frequency Distribution do NLTK:

from nltk.probability import FreqDist 
fdist = FreqDist(text) 
print(fdist.most_common(2))

output: [('Eu', 1), ('amo', 1)]

Existem várias tarefas que podemos abordar nesse contexto, incluindo: lemmatizer, pos-tagging, stemmer, stopwords. Mas esses são assuntos para outras aulas.

Como criar um saco de palavras usando o NLTK

O primeiro passo para criar um saco de palavras é importar os módulos necessários para o pré-processamento e criação do saco.    

import nltk
import re

# inserimos um texto 
aleatóriotexto = """France, in Western Europe, encompasses medieval cities, alpine villages and Mediterranean beaches. Paris, its capital, is famed for its fashion houses, classical art museums including the Louvre and monuments like the Eiffel Tower. The country is also renowned for its wines and sophisticated cuisine. Lascaux’s ancient cave drawings, Lyon’s Roman theater and the vast Palace of Versailles attest to its rich history."""     

A seguir iremos dividir o texto em sentenças e agrupar tudo em uma lista:  

# Divide o texto em frases
dataset = nltk.sent_tokenize(texto)

    O pré-processamento deverá remover caracteres indesejados para a criação do saco de palavras:  

#realizando o pré-processamento
for i in range (len(dataset)):
    dataset[i] = dataset[i].lower() #converte todas as palavras para letras minusculas
    dataset[i] = re.sub(r'W', ' ', dataset[i]) # troca tudo que não for uma palavra para um espaço
    dataset[i] = re.sub(r's+', ' ', dataset[i]) # troca tudo que for quebras de linha para um espaço simples
    
print (dataset)

output:

['france in western europe encompasses medieval cities alpine villages and mediterranean beaches ', 'paris its capital is famed for its fashion houses classical art museums including the louvre and monuments like the eiffel tower ', 'the country is also renowned for its wines and sophisticated cuisine ', 'lascaux s ancient cave drawings lyon s roman theater and the vast palace of versailles attest to its rich history ']     

Com o texto já pre-processado podemos criar um histograma. Nada mais é do que uma lista de registros que nos informa a quantidade de vezes que uma palavra aparece no texto:  

# criando um histograma

word2count = {}
for data in dataset:
    words = nltk.word_tokenize(data)
    for word in words:
        if word not in word2count.keys():
            word2count[word] = 1
        else:
            word2count[word] += 1
            
print (word2count)
{'france': 1, 'in': 1, 'western': 1, 'europe': 1, 'encompasses': 1, 'medieval': 1, 'cities': 1, 'alpine': 1, 'villages': 1, 'and': 4, 'mediterranean': 1, 'beaches': 1, 'paris': 1, 'its': 4, 'capital': 1, 'is': 2, 'famed': 1, 'for': 2, 'fashion': 1, 'houses': 1, 'classical': 1, 'art': 1, 'museums': 1, 'including': 1, 'the': 4, 'louvre': 1, 'monuments': 1, 'like': 1, 'eiffel': 1, 'tower': 1, 'country': 1, 'also': 1, 'renowned': 1, 'wines': 1, 'sophisticated': 1, 'cuisine': 1, 'lascaux': 1, 's': 2, 'ancient': 1, 'cave': 1, 'drawings': 1, 'lyon': 1, 'roman': 1, 'theater': 1, 'vast': 1, 'palace': 1, 'of': 1, 'versailles': 1, 'attest': 1, 'to': 1, 'rich': 1, 'history': 1}

    Devemos agora ordenar essa lista considerando aqueles termos que aparecem no texto mais frequentemente:  

# biblioteca para ordenação
import heapq

# ordena a lista para saber qual é a palavra que mais se repete
freq_words = heapq.nlargest(50,word2count, key=word2count.get)
print (freq_words)

output: 

['and', 'its', 'the', 'is', 'for', 's', 'france', 'in', 'western', 'europe', 'encompasses', 'medieval', 'cities', 'alpine', 'villages', 'mediterranean', 'beaches', 'paris', 'capital', 'famed', 'fashion', 'houses', 'classical', 'art', 'museums', 'including', 'louvre', 'monuments', 'like', 'eiffel', 'tower', 'country', 'also', 'renowned', 'wines', 'sophisticated', 'cuisine', 'lascaux', 'ancient', 'cave', 'drawings', 'lyon', 'roman', 'theater', 'vast', 'palace', 'of', 'versailles', 'attest', 'to'] 

O ultimo passo é criar o saco de palavras transcrevendo cada documento para uma informação booleana dizendo se cada palavra do saco de palavras está presente ou não no documento:  

import numpy as np

x = []

for data in dataset:
    vector = []
    for word in freq_words:
        if word in nltk.word_tokenize(data):
            vector.append(1) # se a palavra está contida no documento coloca-se 1
        else:
            vector.append(0) # se a palavra não está contida no documento coloca-se 0
    x.append(vector)
    
y = np.asarray(x)
print (y)

Output:
[[1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0] [1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1]] 

   

Observações importantes

O saco de palavras permite que você utilize classificadores e faça outras análises posteriormente. Criar um saco de palavra não te dá informação alguma instantaneamente.   O saco de palavras utilizando a incidência das palavras pode ser utilizado, porém, esse modelo possui problemas já bem conhecidos. São eles: (1) “perca” de informação sintática, considerando que se trata de uma abordagem estatística. (2) Modelos que consideram a frequência inversa de palavras no conjunto de documentos já provaram ser mais eficientes em muitos casos.

Vinicius dos Santos

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

Deixe uma resposta