É quase um milagre este post sair com tão pouco tempo desde o último, mas o fato é que estou muito empolgado com meus estudos e o curso da Udemy que comentei no ultimo post, tanto que comecei a aplicar algumas coisas na prática!
Hoje quero mostrar um pequeno programa que criei para testar e praticar as técnicas de Machine Learning na categorização de textos. É apenas um protótipo que usa matérias jornalisticas, mas você pode baixa-lo para testar e estudar!
Update: Acabei criando um repositório em meu github para este projetinho, assim fica mais fácil baixar, instalar e testar o código!
Importante: Meu objetivo não é abordar profundamente as diferentes técnicas, abordagens e algoritmos disponíveis pois meu conhecimento ainda não me permite. Ao invés disso quero te mostrar como é possível aplicar tais conhecimentos e, quem sabe, te estimular a também estudar o assunto!
Como você pode ver no aquivo em meu Github, o programa ficou com 152 linhas de código, o que é pouco mas vou me concentrar nas partes relacionadas ao processamento dos dados, treinamento e predição que são o foco do artigo de hoje.
O programa pode ser dividido nas seguintes etapas:
- Baixar e extrair o conteúdo das matérias a partir de um CSV com links e categorias;
- Limpar o conteúdo e deixar apenas o que é relevante para o treinamento do modelo;
- Criar o Bag of Words e treinar o modelo que fará a categorização;
- Categorizar novos links com o modelo treinado!
Com estas etapas em mente, vamos ao que interessa!
1. Baixar e Extrair o Conteúdo (Goose, o achado!)
Se você já criou algum tipo de web scraping sabe como pode ser
Goose, muito amor!
Em uma olhada rápida na documentação percebemos que esse projeto é perfeito para o nosso caso e em 3 linhas ele resolve o problema:
from goose import Goose .... goose = Goose() article = goose.extract(link) text = article.cleaned_text
Basta passar o link da página e o Goose faz o resto! Ele também permite acessar outros atributos da página, como o título da matéria, descriptors e etc. Vale a pena conferir!
Por se tratar de uma tarefa I/O Bound e serem mais de 700 links para baixar, estou usando a classe ThreadPoolExecutor do backport futures. O uso é muito simples e você pode saber mais na documentação do projeto!
2. Limpar o Conteúdo (NLTK)
Com o texto das matérias em mãos, precisamos remover caracteres e palavras que podem atrapalhar o algoritmo de categorização, deixando apenas as palavras que possam contribuir com o "entendimento" do texto. Na primeira etapa removo praticamente tudo que não for texto:
import unicodedata ... def remove_nonlatin(string): new_chars = [] for char in string: if char == '\n': new_chars.append(' ') continue try: if unicodedata.name(unicode(char)).startswith(('LATIN', 'SPACE')): new_chars.append(char) except: continue return ''.join(new_chars) ... text = remove_nonlatin(to_unicode(text))
Depois disso precisamos remover as chamadas stop words que, em resumo, são palavras que se repetem muito em qualquer texto e podem prejudicar a analise feita pelo algoritmo. Isso é feito com a ajuda do projeto NLTK.
O NLTK pode ser instalado via pip mas depois da sua instalação é preciso baixar o pacote stopwords pelo gerenciador do próprio NLTK. Vou deixar lincadas as instruções das 2 etapas.
Depois de tudo instalado, é fácil remover as stop words dos nossos textos:
from nltk.corpus import stopwords ... stops = set(stopwords.words("portuguese")) words = ' '.join([w for w in words if not w in stops])
Por fim, criamos um DataFrame pandas para reunir os links, as categorias e os textos processados:
from pandas import DataFrame ... lines = [] words = pre_processor(link) lines.append((link, categ, words)) ... df = DataFrame(lines) df.columns = ['link', 'categoria', 'texto']
3. Bag of Words
Em resumo rápido, bag of words é um modelo de representação textual que ignora a ordem e a gramática das palavras mas preserva sua multiplicidade. Um exemplo pode nos ajudar:
O texto: "Rafael gosta muito de assistir filmes, a Ana gosta de filmes também" é transformado em uma lista de palavras:
["Rafael", "gosta", "muito", "de", "assistir", "filmes", "a", "Ana", "também"]
Depois disso o modelo gera uma lista com a frequência que cada palavra aparece no texto : [1, 2, 1, 2, 1, 2, 1, 1, 1]
Este processo, de traduzir palavras em números, é necessário pois os algoritmos que vamos usar para classificar os textos só aceitam/"entendem" números. Para nossa sorte, o scikit-learn, uma das principais bibliotecas do tipo em Python, já tem uma classe para ajudar neste processo.
Abaixo vou mostrar a função de treinamento por completo, acredito que seja mais fácil explicar assim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import sklearn from sklearn.externals import joblib from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression from sklearn.feature_extraction.text import CountVectorizer def train(df, fit_file): print "\nTraining..." df = df.dropna() train_size = 0.8 vectorizer = CountVectorizer( analyzer="word", tokenizer=None, preprocessor=None, stop_words=None ) logreg = LogisticRegression() pipe = Pipeline([('vect', vectorizer), ('logreg', logreg)]) X_train, X_test, Y_train, Y_test = sklearn.model_selection.train_test_split( df.texto, df.categoria, train_size=train_size ) pipe.fit(X_train, Y_train) accuracy = pipe.score(X_test, Y_test) msg = "\nAccuracy with {:.0%} of training data: {:.1%}\n".format(train_size, accuracy) print msg pipe.fit(df.texto, df.categoria) joblib.dump(pipe, fit_file) |
Das linhas 11 a 16 criamos a instância que vai montar o modelo bag of words dos textos que baixamos, mas por enquanto nada foi feito.
Na linha 17 é criada a instância que efetivamente vai analisar nossos dados e fazer predições. Como o nome sugere, será usada uma técnica chamada Regressão Logística para fazer a classificação dos textos. Eu testei algumas técnicas diferentes de classificação, mas esta apresentou os melhores resultados, chegando a 84% de acerto! Você pode conferir os teste que fiz neste link aqui!
Na linha 18 reunimos os 2 processos anteriores em um pipeline. Isto foi necessário para facilitar a preservação e armazenamento do modelo treinado. Vamos falar disso daqui a pouco!
Da linha 19 a 21 usamos uma função do scikit-learn para dividir nossa massa de dados em 80% para treino e 20% para testar a precisão do modelo.
Da linha 22 a 25 nós treinamos nosso modelo, avaliamos a precisão do mesmo e mostramos essa informação no terminal.
Finalizando, na linha 26 o modelo é treinado novamente, mas agora com 100% dos dados, e na linha 27 usamos outra ferramenta do scikit-learn para salvar o modelo treinado em disco pois não precisamos refazer todo este processo toda vez que o programa for usado.
Da linha 19 a 21 usamos uma função do scikit-learn para dividir nossa massa de dados em 80% para treino e 20% para testar a precisão do modelo.
Da linha 22 a 25 nós treinamos nosso modelo, avaliamos a precisão do mesmo e mostramos essa informação no terminal.
Finalizando, na linha 26 o modelo é treinado novamente, mas agora com 100% dos dados, e na linha 27 usamos outra ferramenta do scikit-learn para salvar o modelo treinado em disco pois não precisamos refazer todo este processo toda vez que o programa for usado.
4. Pronto para Uso!
Finalmente! O programa esta pronto para categorizar novos textos! Vamos ver a função predict!
1 2 3 4 5 6 7 8 9 | def predict(url, fit_file): pipe = joblib.load(fit_file) words = pre_processor(url) resp = pipe.predict([words]) print "\nCategory: %s \n" % resp[0] resp = zip(pipe.classes_, pipe.predict_proba([words])[0]) resp.sort(key=lambda tup: tup[1], reverse=True) for cat, prob in resp: print "Category {:16s} with {:.1%} probab.".format(cat, prob) |
A função recebe como parâmetros uma URL (que vamos categorizar) e o caminho para o arquivo salvo em disco com nosso modelo treinado.
A linha 2 carrega o pipeline que salvamos no disco como o CountVectorizer e o LogisticRegression.
A linha 3 usa uma função para baixar e processar o texto da URL fornecida. Basicamente os passos 1 e 2 desse artigo.
A linha 4 usa nosso pipeline para criar o bag of words do texto e fazer a predição de qual a categoria que melhor representa este texto.
As linhas de 6 a 9 mostram todas as categorias que nosso modelo conhece e as respectivas probabilidades em relação ao texto que esta sendo categorizado!
Vamos ver como funciona!
Agora que já falamos sobre as principais partes do programa, vamos ve-lo em ação!
Para testar no seu computador, alem de baixar o arquivo do programa e a lista de links, você precisará instalar as dependências, tudo via pip install:
Para testar no seu computador, alem de baixar o arquivo do programa e a lista de links, você precisará instalar as dependências, tudo via pip install:
- futures
- goose-extractor
- pandas
- nltk
- scikit-learn
O programa usa 3 parâmetros, FILE é o caminho para o arquivo CSV com os links das matérias que você pode baixar aqui. O formato do arquivo é muito simples e você pode montar o seu com outros links e categorias!
TRAIN é o nome do arquivo com o modelo treinado. Se o arquivo já existir o programa usa o modelo existente, caso contrário ele vai baixar os dados e processar.
Primeiro vamos baixar os dados, processá-los e treinar nosso modelo. Esta é a saída do programa mostrando a quantidade de palavras em cada matéria.
Depois de algum tempo baixando e processando os dados, o programa nos mostra que ele salvou o arquivo bag_words.csv com os dados processados e a precisão do modelo após o treinamento. Chegamos a 79%!
Agora vamos testar se ele acerta a categoria desta matéria do G1 sobre tecnologia:
Concluindo (Finalmente!)
Eu adoraria ficar aqui e te mostrar vários exemplos de como o programa acerta a categoria de várias matérias, mas você deve estar bem cansado, eu estou kkk!
Bom, espero ter atingido meu objetivo e pelo menos motivar você a também estudar o assunto! Deixei vários links ao longo do texto e nas referencias para te ajudar a entender melhor do que estamos falando!
Fique a vontade e comente o que achou aqui em baixo e se você souber como posso melhorar a precisão dos modelos usados, será de grande ajuda!
Nenhum comentário:
Postar um comentário