Coletânea Python do ZERO às Redes Neurais Artificiais
Redes Neurais Artificiais
Processamento de Linguagem Natural – Minerando
Emoções
Uma das aplicações mais interessantes quando se trata de aprendizado de
máquina simples é o chamado processamento de linguagem natural, é o processo de reconhecimento de padrões a partir de textos, de forma que possamos treinar uma rede neural a minerar informações a partir dos mesmos.
Áreas como, por exemplo, direito, tem feito o uso de tais modelos como
ferramenta para extração de informações relevantes a partir de textos jurídicos enormes, agilizando dessa forma seus processos. Importante salientar que o uso de redes neurais para tal fim jamais irá substituir os profissionais deste meio, até porque em direito sempre a palavra final e de maior peso se dá pela interpretação do juiz quanto ao caso.
Obviamente um computador não possui nenhuma capacidade de
abstração para entender de fato uma linguagem natural, porém, graças a gramática das linguagens, existem formas padronizadas de se transliterar qualquer coisa em forma de texto. A máquina por sua vez, terá plenas condições de identificar e aprender os padrões explícitos da linguagem, tendo a partir disto, capacidade de minerar informações em meio ao texto.
Partindo para prática, por meio da biblioteca nltk entenderemos passo a
passo como o processo de mineração de dados.
Para este projeto, inicialmente iremos criar os respectivos arquivos (caso
você opte por o fazer modularizado) e realizar as devidas importações da bibliotecas, módulos e pacotes envolvidos neste processo.
Inicialmente, em nosso arquivo mineração.py, executamos o comando
import nltk para importar a mesma. Da mesma forma, por meio do comando from base import base estamos importando o conteúdo da variável base, de nosso pacote base.py
Note no comentário situado na linha 3, caso você ainda não tenha o feito,
é interessante descomentar essa linha e a executar para que a biblioteca nltk baixe todas as suas dependências. Parametrizando a função download com ‘popular’ será feito o download das dependências mais comuns em pronetos, de acordo com sua preferência você pode parametrizar a mesma com ‘all’, sendo assim, serão baixadas todas as dependências da biblioteca nltk. Uma vez feita essa etapa, de fato temos condições de prosseguir com o código sem erros.
Em seguida é criada a variável stopwords, que por sua vez recebe como
atributo o dicionário de stopwords em língua portuguesa por meio do comando exibido acima.
Entendendo de forma simples, stopwords nada mais são do que palavras
que consideraremos irrelevantes ou que até mesmo atrapalham o processo de mineração.
Em nosso arquivo base.py temos uma variável de nome base que,
desculpe a redundância, basicamente guarda dados em forma de elementos de uma lista.
Repare que cada elemento está em um formato onde existe uma string
com uma mensagem seguido da respectiva descrição quanto ao tipo de emoção. Para esse exemplo básico trabalharemos com uma base de dados bastante reduzida e categorizadas em apenas duas possíveis emoções, positiva ou negativa.
Explorando a variável stopwords podemos entender que aqui são
filtrados e guardados todas as palavras que podem ser eliminadas do contexto sem atrapalhar os padrões necessários.
De nossa base de dados, composta por 34 frases com seus respectivos significados em emoções, foram filtrados 204 elementos os quais não representam relevância ao contexto da mineração.
Em seguida criamos a função que irá, nessa fase de tratamento de nossos
dados, remover as stopwords de nossa base de dados.
Para isto, criamos uma função de nome removestopwords( ) que
receberá um texto. Dentro de seu corpo é criada uma variável de nome frases que inicialmente é uma lista vazia.
Em seguida, por meio de um laço for, percorreremos cada elemento
atribuído a texto em sua forma de palavra = emoção. Isso é feito por meio da variável removesw que realiza uma verificação onde, cada elemento de cada palavra que não constar em nossa lista de stopwords será adicionado para a lista de nossa variável frases, retornado essa variável composta de todos os elementos que estiverem nessas condições.
Na sequência realizamos um segundo tratamento, agora reduzindo as
palavras quanto ao seu radical, e isso é feito por meio da função RSLPStemmer( ). Basicamente o que é feito aqui é a extração dos sufixos das palavras para tornar nossa base de dados ainda mais enxuta.
Por exemplo, o radical das palavras alegria, alegre, alegrando, alegrar,
alegremente é alegr. Para o interpretador é apenas isto que importa, inclusive não tendo nenhum prejuízo quanto a eliminação desses sufixos.
O processo de redução/radicalização será feito por meio da função
reduzpalavras( ) que recebe como parâmetro um texto a ser processado. Inicialmente é criada a variável stemmer que chama a função RSLPStemmer( ) da biblioteca nltk.
Em seguida é criada a variável frases_reduz que inicialmente trata-se de uma lista vazia.
Exatamente da mesma forma como a função anterior foi criada, aqui
percorreremos cada elemento verificando se o mesmo não está em nossa lista negra de stopwords e se pode ser reduzido pela função RSLPStemmer( ), adicionando esses elementos para lista atribuída a frases_redux, retornando a mesma.
Encerrando essa etapa, criamos no escopo global de nosso código a
variável frases_reduzidas, que será nossa base de dados pós-tratamento.
Por meio do explorador de variáveis podemos ver que agora
frases_reduzidas possui seus dados em forma onde cada string teve seus elementos separados, reduzidos e associados a sua respectiva emoção.
Dando sequência, é criada a função buscapalavras( ) que recebe uma
frase. Basicamente o que essa função fará é receber uma frase, aplicar o pré-processamento, fazendo uma varredura na mesma a preparando para comparar com nossa base de dados.
Note que assim como nas funções anteriores, os dados são processados
sempre respeitando a equivalência das palavras contidas na frase com sua respectiva emoção.
Na sequência é criada uma variável de nome palavras, que chama a
função buscapalavras( ) parametrizando a mesma com o conteúdo de frases_reduzidas.
Via explorador de variáveis é possível visualizar de forma clara a base
de palavras geradas, palavras estas que serão referência para as devidas associações da mineração.
Seguindo com nosso exemplo, podemos também criar funções dedicadas
a nos retornar a frequência com que certas palavras aparecem em nossos dados assim como as palavras totalmente únicas.
Tenha em mente que, em aplicações reais, nossa base de dados será
muito maior, e essa diferença de incidência/frequência com que certas palavras são assimiladas influencia a margem de acertos de nosso interpretador no que diz respeito as previsões geradas pelo mesmo.
Para a frequência de palavras criamos a função buscafrequencia( ) que
recebe palavras como parâmetro. Dentro do corpo da função, temos a variável freq._palavras que chama a função FreqDist( ) da biblioteca nltk, parametrizando a mesma com palavras, retornando freq._palavras.
Nos mesmos moldes é criada a função buscapalavrasunicas( ) que recebe
como parâmetro para si frequencia. No corpo da função temos uma variável de nome freq que nada mais faz do que pegar em forma numérica os dados de frequência, uma vez que os mesmos estão dispostos em forma parecida a de um dicionário, retornando esse valor.
Terminados todos os tratamentos dos dados de nossa base, assim como
as validações necessárias, hora de criar o mecanismo o qual irá de fato receber um texto a ser processado.
Para isso criamos a função extrator( ) que recebe um documento. Dentro
do corpo dessa função inicialmente criamos uma variável de nome doc que recebe documento em forma de set.
Em seguida criamos uma variável de nome características que
inicialmente é um dicionário vazio.
Na sequência por meio do laço for, iremos percorrer cada palavra de
palavras únicas realizando a comparação da mesma com as palavras que estarão em documento. Isso internamente nada mais é do que o rápido tratamento do texto recebido, de forma que ele terá seus elementos comparados um a um com todas as palavras já classificadas em nossa base de dados, marcando as mesmas como True quando houverem combinações e como False quando não houverem. Havendo combinações, nosso interpretador fará a associação com a emoção referente aquela palavra.
Por fim é criada a variável baseprocessada que chama a função
apply_features( ) do módulo classify da biblioteca nltk, parametrizando a mesma com o conteúdo de extrator e de frases reduzidas, justamente para comparar a equivalência das mesmas em busca de emoções.
Encerrando nossa linha de raciocínio, uma vez criada toda a estrutura
que processará os elementos, podemos finalmente criar o mecanismo que lê os dados e os encaminha para processamento.
De início criamos uma variável de nome teste que pede ao usuário que
digite como o mesmo está se sentindo. Também é criada uma variável de nome teste_redux que inicialmente é uma simples lista vazia.
Da mesma forma também é criada a variável redux, que chama a função
RSLPStemmer( ) sem parâmetros mesmo.
Na sequência por meio dos laço for realizamos aquele pré-tratamento
dos dados, lendo os mesmos da variável teste, reduzindo e radicalizando os mesmos, guardando os mesmos em teste_redux.
Por fim, criamos uma variável de nome resultado que chama a função
extrator( ) ativando toda a cadeia de processamento sobre os dados de teste_redux. O resultado é exibido por meio da função print( ) parametrizada com a função classify de nosso classificador, sobre os dados de resultado.
* Repare que para este exemplo, todo o processamento interno realizado pela máquina é totalmente implícito, por meio das métricas das funções da própria biblioteca nltk. Nos exemplo subsequentes, usaremos de outras bibliotecas que nos permitirão uma margem maior de customização dos meios e métodos de processamento de nossos dados.
Pondo em teste nosso código, ao executar o mesmo é exibida via console
a mensagem definida em nossa variável teste. Supondo que o usuário tenha digitado “Eu me sinto bem”, o resultado, como esperado, é emoção positiva.
Código Completo: