Resume meu texto?

thaisplicando

Thais Ribeiro

Posted on July 26, 2022

Resume meu texto?

Olá pessoas incríveis da internet, tudo bem com vocês? Trago hoje mais um artigo explorando o mundo de inteligência artificial, onde vamos aprender a sumarizar textos com a ajuda de NLP (Natural Language Processing).
Para começarmos, precisamos entender que sumarização é basicamente a construção de um resumo, onde dado um texto, se produz outro texto que condense o sentido do texto original, focando apenas nos pontos principais.

Atualmente, usamos textos sumarizados para facilitar a análise dos dados que recebemos, por exemplo, uma grande organização usa desse processo para trazer as partes importantes de feedbacks para gerar insights, outro exemplo é quando precisamos resumir partes importantes de um determinado texto para apresentar em links de matérias, blogs.

Apesar de parecer trivial, é uma atividade bastante comum que geralmente é feita de forma manual, isso torna o processo mais tedioso e lento, então como podemos resolver isso? Com os avanços tecnológicos, hoje conseguimos fazer isso de forma automática com a ajuda de Machine Learning, especificadamente, usando processamento de linguagem natural.

Existem duas abordagens que são usadas para a sumarização: o resumo abstrativo que consiste na geração de novas frases a partir do texto original e a sumarização extrativa, onde identificamos frases importantes no texto e extraímos gerando o resumo, está última será a que vamos usar.

Vamos construir uma API, usando FastAPI, onde teremos como entrada, tanto texto como url, vamos começar dividindo o artigo em partes. A primeira parte vamos tratar os dados que recebemos, na segunda parte vamos fazer a parte de sumarização.

Colocando a mão na massa

Lidando com os dados

  • Vamos iniciar instalando o framework do fastAPI
pip install fastapi

Enter fullscreen mode Exit fullscreen mode
  • Vamos precisar também de um servidor ASGI
pip install uvicorn

Enter fullscreen mode Exit fullscreen mode

Nosso projeto vai ter a seguinte estrutura:

text_summarizer_api/ # raiz
┣ api/
┃ ┣ routers/
┃ ┃ ┗ summarization.py # rotas da API (POST/GET)
┃ ┣ schemas/
┃ ┃ ┗ summarization_schema.py # Formato da entrada de dados
┃ ┣ services/
┃ ┃ ┗ summarization_service.py # Sumarização de texto
┣ main.py # Inicial
┗ requirements.txt

Enter fullscreen mode Exit fullscreen mode

Começamos construindo nosso serviço de sumarização, lembrando que nessa parte vamos tratar os dados:

Dado o seguinte payload, percebemos que podemos receber tanto texto como url, além de ser obrigatório o idioma, para que a sumarização seja feita de forma correta.

Image description

Vamos começar com o método get_summarization para identificar que tipo de dado estamos recebendo:

async def get_summarization(summarization):
    formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)

Enter fullscreen mode Exit fullscreen mode

Se passarmos uma URL, vamos precisar fazer um web scraping para extrair as informações de texto que vamos precisar e para fazer essa extração vamos usar um módulo chamado BeautifulSoup.

pip install beautifulsoup4 requests
Enter fullscreen mode Exit fullscreen mode

Não abordaremos de forma profunda como é feita essa raspagem, talvez em outro artigo, mas para atender nossa demanda, faremos o seguinte:

  • Enviaremos uma requisição na url recebida
  • Vamos recuperar o conteúdo HTML como texto
  • Examinaremos a estrutura recebida em soup e vamos buscar pelo elemento especifico
  • Por fim, vamos extrair os textos e realizar uma limpeza, removendo todos os caracteres especiais, acentos e espaçamentos inutilizados.
from bs4 import BeautifulSoup as bs

def scraping_text(url):
    response = requests.get(url)
    soup = bs(response.content, 'lxml')
    paragraph = soup.find_all(name='p', attrs= {"class": "pw-post-body-paragraph"}, limit=40)
    joined_text = ''.join([p.text.strip().lower() for p in paragraph])
    joined_text = format_text(joined_text)
    return joined_text

def format_text(text):
    formatted_text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8')
    formatted_text = re.sub(r'\[[0-9]*\]', ' ', formatted_text)
    formatted_text = re.sub(r'\s+', ' ', formatted_text)
    return formatted_text

Enter fullscreen mode Exit fullscreen mode

Pronto, temos os dados tratados e agora entramos na segunda parte do artigo.

Sumarização

Para começar nosso processo de sumarização, precisamos instalar e importar as bibliotecas necessárias:

pip install nltk
Enter fullscreen mode Exit fullscreen mode

NLTK, significa Natural Language Toolkit, ou seja, um conjunto de ferramentas para linguagem natural que contem vários algoritmos implementados para trabalhar com linguagem natural.

Usaremos os pacotes Corpus e os Tokenizers, quando falamos em “tokenização”, falamos em dividir um texto em uma série de tokens, já um corpus de texto é um grande corpo de texto ou coleção de texto, que podem ser conjuntos de dados, como obras de um autor, poemas, ou um conjunto de palavras de paradas.

O NLKT inclui alguns corpora que são essas listas de palavras irrelevantes, ou seja, palavras de alta frequência, que podemos filtrar em um documento antes de iniciar o processamento, no nosso caso vamos filtrar essas palavras de parada, adicioná-las a um dicionário python para verificar a frequência com que elas aparecem no texto antes de serem removidas.

Vamos usar este dicionário sobre cada frase para saber quais tem o conteúdo mais relevante no texto geral.

import re
import requests
from bs4 import BeautifulSoup as bs
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize, sent_tokenize

async def get_summarization(summarization):
    formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
    stop_words = set(stopwords.words(str(summarization.language)))
    frequences_word = handle_frequence_word(stop_words, formatted_text)
    print('Dicionário de frequencia de palavras de parada', frequences_word)

def handle_frequence_word(stop_words, text):
    words = word_tokenize(text)
    frequences_word = dict()
    for word in words:
        if word in stop_words:
            continue
        if word in frequences_word:
            frequences_word[word] += 1
        else:
            frequences_word[word] = 1

    return frequences_word

def scraping_text(url):
    response = requests.get(url)
    soup = bs(response.content, 'lxml')
    paragraph = soup.find_all(name='p', attrs= {"class": "pw-post-body-paragraph"}, limit=40)
    joined_text = ''.join([p.text.strip().lower() for p in paragraph])
    joined_text = format_text(joined_text)
    return joined_text

def format_text(text):
    formatted_text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8')
    formatted_text = re.sub(r'\[[0-9]*\]', ' ', formatted_text)
    formatted_text = re.sub(r'\s+', ' ', formatted_text)
    return formatted_text
Enter fullscreen mode Exit fullscreen mode

Agora, lidaremos com as sentenças, ou seja, os tokens de frases mais tarde usaremos essa lista no resumo do texto. Nesta avaliação analisaremos a frequência de ocorrência de cada termo, logo, estaremos pontuando cada frase por suas palavras; isto é, somando a frequência de cada palavra importante encontrada na frase.

async def get_summarization(summarization):
    formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
    stop_words = set(stopwords.words(str(summarization.language)))
    frequences_word = handle_frequence_word(stop_words, formatted_text)
    sentences = sent_tokenize(formatted_text)
    frequences_sentence = handle_sentences(frequences_word, sentences)

def handle_sentences(frequences_word, sentences):
    frequences_sentence = dict()
    for sentence in sentences:
        for word, freq in frequences_word.items():
            if word in sentence:
                if sentence in frequences_sentence:
                    frequences_sentence[sentence] += freq
                else:
                    frequences_sentence[sentence] = freq

    return frequences_sentence
Enter fullscreen mode Exit fullscreen mode

Para concluirmos a parte de sumarização, criaremos um resumo escolhendo 30% das frases mais ponderadas.

select_lenght = int(len(frequences_sentence) * 0.3) # peso 30%
Enter fullscreen mode Exit fullscreen mode

Com a ajuda da função nlargest do módulo heapq, vamos retornar o número especificado de maiores elementos de um iterável, no nosso caso, encontraremos os maiores n elementos, aqui o valor da lista com as frases com peso 30% em cima do iterável que é nosso dicionário de sentenças.

summary = nlargest(select_lenght, frequences_sentence, key=frequences_sentence.get)
Enter fullscreen mode Exit fullscreen mode

Por fim, percorremos o sumário gerado concatenando em uma string, sendo essa o resultado do nosso resumo.

O código completo do nosso service fica assim:

import re
import requests
import unicodedata
from bs4 import BeautifulSoup as bs
from heapq import nlargest
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize, sent_tokenize

async def get_summarization(summarization):
    formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
    stop_words = set(stopwords.words(str(summarization.language)))
    frequences_word = handle_frequence_word(stop_words, formatted_text)
    sentences = sent_tokenize(formatted_text)
    frequences_sentence = handle_sentences(frequences_word, sentences)
    return get_resume(frequences_sentence)

def scraping_text(url):
    response = requests.get(url)
    soup = bs(response.content, 'lxml')
    paragraph = soup.find_all(name='p', attrs= {"class": "pw-post-body-paragraph"}, limit=40)
    joined_text = ''.join([p.text.strip().lower() for p in paragraph])
    joined_text = format_text(joined_text)
    return joined_text

def format_text(text):
    formatted_text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8')
    formatted_text = re.sub(r'\[[0-9]*\]', ' ', formatted_text)
    formatted_text = re.sub(r'\s+', ' ', formatted_text)
    return formatted_text

def handle_frequence_word(stop_words, text):
    words = word_tokenize(text)
    frequences_word = dict()
    for word in words:
        if word in stop_words:
            continue
        if word in frequences_word:
            frequences_word[word] += 1
        else:
            frequences_word[word] = 1

    return frequences_word

def handle_sentences(frequences_word, sentences):
    frequences_sentence = dict()
    for sentence in sentences:
        for word, freq in frequences_word.items():
            if word in sentence:
                if sentence in frequences_sentence:
                    frequences_sentence[sentence] += freq
                else:
                    frequences_sentence[sentence] = freq

    return frequences_sentence

def get_resume(frequences_sentence):
    select_lenght = int(len(frequences_sentence) * 0.3) # peso 30%
    summary = nlargest(select_lenght, frequences_sentence, key=frequences_sentence.get)
    final = [word for word in summary]
    summary = ''.join(final)
    return summary

Enter fullscreen mode Exit fullscreen mode

Finalizando…

Para finalizarmos nossa API, agora vamos consumir os serviços nas rotas e gerar endpoints de entrada, o código completo está no meu github.
Para finalizarmos nossa API, agora vamos consumir os serviços nas rotas e gerar endpoints de entrada, o código completo está no meu github.

Iniciaremos nosso servidor: uvicorn main:app --reload e podemos ter acesso a API: http://127.0.0.1:8000/docs

Image description

Image description

Obtemos a saída resumida e mantendo a lógica do texto original

Image description

Bom pessoal, não aprofundamos na criação de API’s porque o que quero mostrar para vocês é como podemos automatizar alguns processos morosos utilizando machine learning, espero que tenham gostado do conteúdo e por hoje, ficamos por aqui.

💖 💪 🙅 🚩
thaisplicando
Thais Ribeiro

Posted on July 26, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Resume meu texto?
ia Resume meu texto?

July 26, 2022