Introdução ao Apache Mahout

Aprendizagem por máquina escalável, de fácil uso comercial para a construção de aplicativos inteligentes

Outrora domínio exclusivo de acadêmicos e corporações com grandes orçamentos de pesquisa, as aplicações inteligentes que aprendem a partir de dados e contribuição de usuário estão se tornando mais comuns. A necessidade por técnicas de aprendizagem por máquina, como armazenamento em cluster, filtragem colaborativa, e categorização nunca foi maior, seja para localizar aspectos em comum entre grandes grupos de pessoas ou rotular automaticamente grandes volumes de conteúdo de Web. O projeto Apache Mahout visa facilitar e acelerar aplicações inteligentes de construção. O Co-fundador do Mahout, Grant Ingersoll, apresenta os conceitos básicos de aprendizagem por máquina e, em seguida, demonstra como usar o Mahout para agrupar documentos, fazer recomendações, e organizar conteúdos.

Grant Ingersoll, Senior software engineer, Center for Natural Language Processing at Syracuse University

Grant IngersollGrant Ingersoll is a senior software engineer at the Center for Natural Language Processing at Syracuse University. Grant's programming interests include information retrieval, question answering, text categorization, and extraction. He is a committer and speaker on the Lucene Java project.



25/Set/2009

Cada vez mais, o sucesso de empresas e indivíduos na era da informação depende da rapidez e eficiência com as quais eles transformam quantidades de dados em informações ativas. Seja para processar centenas ou milhares de mensagens de e-mail pessoais por dia ou vasculhar a intenção de usuários a partir de petabytes de weblogs, a necessidade de ferramentas que possam organizar e aprimorar dados nunca foi maior. Nisso, situa-se a premissa e a promessa do campo da aprendizagem por máquina e do projeto que esse artigo apresenta: Apache Mahout (consulte Resources).

A aprendizagem por máquina é um subcampo da inteligência artificial referente a técnicas que permitem os computadores melhorarem seus resultados com base em experiências anteriores. O campo é estritamente relacionado à mineração de dados e geralmente utiliza técnicas de estatística, teoria da probabilidade, reconhecimento de padrões, e uma série de outras áreas. Embora a aprendizagem por máquina não seja um campo recente, encontra-se definitivamente em crescimento. Muitas grandes empresas, inclusive a IBM®, Google, Amazon, Yahoo! e Facebook, implementaram algoritmos de aprendizagem por máquina em suas aplicações. Muito, muito mais empresas se beneficiariam do aproveitamento da aprendizagem por máquina em suas aplicações para aprender com usuários e situações passadas.

Após fornecer uma breve visão geral de conceitos de aprendizagem por máquina, apresentarei as características, história, e objetivos do projeto Apache Mahout. Em seguida, mostrarei como utilizar o Mahout para realizar tarefas de aprendizagem por máquina interessantes utilizando o conjunto de dados Wikipedia disponíveis gratuitamente.

Aprendizagem por máquina 101

Os usos da aprendizagem por máquina abrangem desde jogos passando pela detecção de fraudes até a análise da bolsa de valores. É utilizada para construir sistemas como os da Netflix e Amazon que recomendam produtos aos usuários com base em compras passadas, ou sistemas que encontram todos os artigos de notícias similares em um determinado dia. Também pode ser usada para categorizar páginas de Web automaticamente conforme o gênero (esportes, economia, guerra, e daí por diante) ou marcar mensagens de e-mail como spam. Os usos da aprendizagem por máquina são mais numerosos do que eu posso abordar neste artigo. Caso haja interesse em explorar o campo com mais profundidade, incentivo você a consultar a seção Recursos.

Diversas abordagens à aprendizagem por máquina são utilizadas para solucionar problemas. Concentrarei nas duas mais utilizadas comumente — aprendizagem supervisionada e não supervisionada—pois elas são as principais suportadas pelo Mahout.

A aprendizagem supervisionada recebe a tarefa de aprender uma função a partir de dados de treinamento rotulados para prever o valor de qualquer entrada válida. Exemplos comuns de aprendizagem supervisionada incluem classificar mensagens de e-mail como spam, rotular páginas de Web de acordo com seu gênero, e reconhecer escrita à mão. Muitos algoritmos são utilizados para criar aprendizes supervisionados, sendo os mais comuns as redes neurais, Máquinas de Vetor de Suporte (SVMs), e classificadores Naive Bayes.

Aprendizagem não supervisionada, como se pode deduzir, recebe a tarefa de atribuir sentido a dados sem quaisquer exemplos do que é correto ou incorreto. É mais comumente usada para agrupar entrada similar em grupos lógicos. Também pode ser usada para reduzir o número de dimensões em um conjunto de dados para concentrar somente nos atributos mais úteis, ou para detectar tendências. Abordagens comuns à aprendizagem não supervisionada incluem armazenamento em cluster k-Médio, hierárquico, e mapas auto-organizadores.

Neste artigo, focarei em três tarefas específicas de aprendizagem por máquina que o Mahout implementa atualmente. Elas também são três áreas bastante utilizadas em aplicações reais:

  • Filtragem colaborativa
  • Armazenamento em cluster
  • Categorização

Examinarei com mais profundidade cada uma dessas tarefas em nível conceitual, antes de explorar suas implementações no Mahout.

Filtragem colaborativa

Filtragem colaborativa (CF) é uma técnica popularizada pela Amazon e outras, que utiliza informações de usuário, como por exemplo, classificações, cliques e compras para fornecer recomendações a outros usuários do site. A CF geralmente é utilizada para recomendar ao cliente itens como livros, música e filmes, porém também é utilizada em outras aplicações onde agentes múltiplos precisam colaborar para restringir dados. É provável que você tenha visto a CF em ação na Amazon, conforme mostrado na Figura1:

Figura 1. Exemplo de filtro colaborativo na Amazon
Exemplo de filtro colaborativo na Amazon

Dado um conjunto de usuários e itens, as aplicações de CF fornecem recomendações ao usuário atual do sistema. Quatro modos de se gerar recomendações são típicos:

  • Baseado em usuário: Itens recomendados através da localização de usuários similares. Isso é geralmente mais difícil de mensurar, devido à natureza dinâmica dos usuários.
  • Baseado em item: Calcula similaridades entre itens e faz recomendações. Normalmente, os itens não mudam muito, portanto, isso pode ser calculado offline.
  • Slope-One: Uma abordagem de recomendação baseada em item muito rápida e simples aplicável quando usuários possuem determinadas classificações (e não apenas preferências de variáveis booleanas).
  • Baseado em modelo: Fornece recomendações com base no desenvolvimento de um modelo de usuários e suas classificações.

Todas as abordagens terminam por calcular uma noção de similaridades entre usuários e seus itens classificados. Há muitas maneiras de calcular similaridade, e a maioria dos sistemas de CF permitem inserir diferentes medidas de modo que seja possível determinar qual delas funciona melhor para os seus dados.

Armazenamento em cluster

Considerando grandes conjuntos de dados, sejam em textos ou numéricos, são geralmente úteis para reunir, ou agrupar, itens similares de forma automática. Por exemplo, dadas todas as notícias do dia de todos os jornais dos EUA, pode-se querer agrupar todos os artigos sobre o mesmo assunto automaticamente; é possível, então, optar por concentrar em clusters e histórias específicos sem precisar avançar por uma série de itens não relacionados. Outro exemplo: Dado a saída de sensores em uma máquina com o tempo, é possível agrupar as saídas para determinar operação normal versus operação problemática, pois todas as operações normais se agrupariam e as operações anormais ficariam em clusters distantes.

Como a CF, o armazenamento em cluster calcular a similaridade entre os itens na coleção, porém sua única tarefa é agrupar itens similares. Em muitas implementações de armazenamento em cluster, os itens na coleção são representados como vetores em um espaço n-dimensional. Dado os vetores, pode-ser calcular a distância entre dois itens usando medidas, como por exemplo, a Distância de Manhattan, a Distância Euclideana, ou similaridade do cosseno. Então, os clusters reais podem ser calculados através da reunião dos itens que estão próximos em distância.

Há muitas abordagens para se calcular os clusters, cada uma com suas compensações. Algumas abordagens operam de baixo para cima, formando grandes clusters a partir dos pequenos, enquanto que outras dividem um único grande cluster em clusters cada vez menores. Ambas possuem critérios para abandonar o processo em algum ponto antes que elas se dividam em uma representação de cluster trivial (todos os itens em um cluster ou todos os itens em seu próprio cluster). Abordagens populares incluem armazenamento em cluster k-Médio e hierárquico. Conforme demonstrarei depois, o Mahout possui diversas abordagens diferentes de armazenamento em cluster.

Categorização

O objetivo da categorização (frequentemente também chamada classificação) é rotular documentos não visualizados, desse modo, reunindo-os. Muitas abordagens de classificação em aprendizagem por máquina calculam uma gama de dados estatísticos que associam as características de um documento ao rótulo especificado, criando, assim, um modelo que pode ser usado mais tarde para classificar documentos não visualizados. Por exemplo, uma abordagem simples à classificação pode acompanhar as palavras associadas ao rótulo, bem como o número de vezes que essas palavras são visualizadas para um determinado rótulo. Então, quando um novo documento é classificado, as palavras no documento são visualizadas no modelo, as probabilidades são calculadas, e o melhor resultado é a saída, normalmente junto com uma pontuação indicando a certeza de que o resultado está correto.

Características para classificação podem incluir palavras, medidas para essas palavras (com base na frequência, por exemplo), partes do discurso, e daí por diante. É claro que características podem realmente ser qualquer coisa que ajude a associar um documento a um rótulo e que possa ser incorporada ao algoritmo.

O campo de aprendizagem por máquina é grande e robusto. Em vez de concentrar ainda mais na parte teórica, o que é impossível para fazer a devida justiça até aqui, prosseguirei e aprofundarei no Mahout e em seu uso.


Introdução ao Mahout

Apache Mahout é um novo projeto de fonte aberta pela Apache Software Foundation (ASF) com o objetivo primário de criar algoritmos de aprendizagem por máquina escaláveis que sejam livres para ser usados sob a licença Apache. O projeto está completando seu segundo ano, com um release público. O Mahout contém implementações para armazenamento em cluster, categorização, CF, e programação evolucionária. Além disso, quando prudente, ele usa a biblioteca Apache Hadoop para permitir que o Mahout escale de forma efetiva na nuvem (consulte Resources).

História do Mahout

O que compõe um nome?

Um mahout é uma pessoa que mantém um elefante e o usa como transporte. O nome Mahout origina do uso (no passado) do Apache Haddop do projeto — que tem um elefante amarelo como logotipo — para escalabilidade e tolerância de falhas.

O projeto Mahout foi iniciado por várias pessoas envolvidas na comunidade Apache Lucene (busca de fonte aberta) com um interesse ativo em aprendizagem por máquina e um anseio por implementações robustas, bem-documentadas e escaláveis de algoritmos de aprendizagem por máquina para armazenamento em cluster e categorização. Inicialmente, a comunidade foi conduzida pelo documento "Map-Reduce for Machine Learning on Multicore" por Ng et al (consulte Resources), porém, desde então, evoluiu para abranger abordagens de aprendizagem por máquina muito mais amplas. O Mahout também visa:

  • Construir e suportar uma comunidade de usuários e contribuidores, de modo que o código dure mais do que qualquer envolvimento de contribuidor particular ou qualquer empresa privada ou fundo universitário.
  • Concentra-se em casos de uso prático do mundo real em oposição a pesquisas de vanguarda ou técnicas não comprovadas.
  • Fornece documentação de qualidade e exemplos.

Recursos

Apesar de relativamente novo em termos de fonte aberta, o Mahout já possui uma grande quantidade de funcionalidade, especialmente em relação a armazenamento em cluster e CF. Os recursos primários do Mahout são:

Algumas palavras sobre Map-Reduce

Map-Reduce é uma API de programação distribuída em que a Google foi pioneira e implementada no projeto Apache Hadoop. Combinado com um sistema de arquivo distribuído, ele geralmente torna os problemas de paralelização mais fáceis, fornecendo aos programadores uma API bem-definida para descrever tarefas de computação paralelas. (Consulte Resources para obter mais informações.)

  • Taste CF. Taste é um projeto de fonte aberta para CF iniciado por Sean Owen na SourceForge e doado à Mahout em 2008.
  • Diversos Map-Reduce possibilitaram armazenamento em cluster, incluindo K-Means, fuzzy k-Means, Canopy, Dirichlet, and Mean-Shift.
  • Implementações de classificação de Naive Bayes e Naive Bayes Complementar distribuídas.
  • Capacidades de função de adequação distribuídas para programação evolucionária.
  • Bibliotecas matriciais e vetoriais.
  • Exemplos de todos os algoritmos acima.

Introdução ao Mahout

Colocar o Mahout em operação é relativamente fácil. Para iniciar, é preciso instalar os seguintes pré-requisitos:

É necessário, também, o código de amostra deste artigo (consulte Download), que inclui uma cópia do Mahout e suas dependências. Siga esses passos para instalar o código de amostra:

  1. descompacte sample.zip
  2. cd apache-mahout-examples
  3. ant install

O Passo 3 efetua o download dos arquivos Wikipedia necessários e compila o código. O arquivo Wikipedia utilizado possui cerca de 2,5 gigabytes, portanto, a duração do download dependerá de sua largura de banda.


Construindo um mecanismo de recomendação

O Mahout atualmente fornece ferramentas para construir um motor de recomendação através da biblioteca Taste — um motor rápido e flexível para CF. O Taste suporta tanto recomendações com base em usuário como com base em item e apresenta muitas opções para realizar recomendações, bem como interfaces para você definir sua própria. O Taste consiste de cinco componentes primários que operam com Usuários, Items e Preferências:

  • DataModel: Armazenamento para Usuários, Items, e Preferências
  • UserSimilarity: Interface que define a similaridade entre dois usuários
  • ItemSimilarity: Interface que define a similaridade entre dois itens
  • Recommender: Interface que fornece recomendações
  • UserNeighborhood: Interface para calcular uma vizinhança de usuários similares que podem, portanto, ser usadas pelos Recommenders

Esses componentes e suas implementações possibilitam construir sistemas de recomendação complexos para recomendações baseadas em tempo real ou recomendações offline. Recomendações baseadas em tempo real geralmente podem suportar apenas alguns milhares de usuários, enquanto que recomendações offline podem escalar muito mais alto. O Taste até mesmo apresenta ferramentas para aproveitar o Hadoop para calcular recomendações offline. Em muitos casos, essa é uma abordagem razoável que permite atender as demandas de um grande sistema com muitos usuários, itens e preferências.

Para demonstrar a construção de um sistema de recomendação simples, preciso de alguns usuários, itens, e classificações. Para essa finalidade, gerei aleatoriamente um grande conjunto de Usuários e Preferências para os documentos Wikipedia. (Items em linguagem Taste) usando o código em cf.wikipedia.GenerateRatings (incluso na fonte com o código de amostra) e, então, suplementei isso com um conjunto de classificações trabalhadas a mão acerca de um tópico específico (Abraham Lincoln) para criar o arquivo recommendations.txt final incluso na amostra. A ideia por trás dessa abordagem é mostrar como a CF pode guiar leques de um tópico específico a outros documentos de interesse dentro do tópico. No exemplo, os dados são 990 (rotulados de 0 a 989) usuários aleatórios que têm classificações atribuídas aleatoriamente a todos os artigos na coleção, e 10 usuários (rotulados de 990 a 999) que classificaram um ou mais dos 17 artigos na coleção, contendo a frase Abraham Lincoln.

Cuidado com dados artificiais!

O exemplo aqui apresentado contém dados puramente artificiais. Eu mesmo fiz as classificações, simulando 10 usuários reais com interesse em informações em Abraham Lincoln. Embora acredite que o conceito por trás dos dados seja interessante, os dados em si e os valores utilizados não são. Se quiser dados reais, sugiro verificar o projeto GroupLens na Universidade de Minnesota e a documentação do Taste (consulte Resources). Optei por inventar os dados, pois eu queria usar um conjunto de dados individuais em todos os exemplos.

Para começar, demonstrarei como criar recomendações para um usuário, considerando o conjunto de classificações em recommendations.txt. Como é o caso com a maioria dos usos do Taste, o primeiro passo é carregar os dados que contêm as recomendações e armazená-los em um DataModel. O Taste apresenta diversas implementações diferentes de DataModel para se trabalhar com arquivos e bases de dados. Para esse exemplo, facilitarei as coisas e usarei a classe FileDataModel, que espera que cada linha esteja na forma: ID de usuário, ID de item, preferência — em que tanto o ID de usuário como o ID de item são cadeias de caractere, enquanto que a preferência pode ser dupla. Dado um modelo, preciso, portanto, informar ao Taste sobre como ele deve comparar usuários declarando uma implementação UserSimilarity. Dependendo da implementação UserSimilarity usada, pode ser preciso, também, informar ao Taste sobre como inferir preferências na ausência de um cenário explícito para o usuário. A Listagem 1 coloca todas essas palavras em código. (cf.wikipedia.WikipediaTasteUserDemo no código de amostra contém a listagem completa.)

Listagem 1. Criando o modelo e definindo similaridade de usuário
//create the data model 
FileDataModel dataModel = new FileDataModel(new File(recsFile)); 
UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(dataModel); 
// Optional: 
userSimilarity.setPreferenceInferrer(new AveragingPreferenceInferrer(dataModel));

Na Listagem 1, uso PearsonCorrelationSimilarity, que mede a correlação entre duas variáveis, porém outras medidas de UserSimilarity estão disponíveis. A escolha de uma medida de similaridade depende do tipo de dados presentes e de seu teste. Para esses dados, considero esta combinação como a mais funcional enquanto ainda demonstra os problemas. Você encontrará mais informações sobre escolha de uma medida de similaridade no site do Mahout (consulte Resources).

Para finalizar o exemplo, construí uma UserNeighborhood e um Recommender. O UserNeighborhood identifica usuários similares a meu usuário e é passado ao Recommender, que, então, realiza a tarefa de criar uma lista de classificação de itens recomendados. A Listagem 2 captura essas ideias em código:

Listagem 2. Gerando recomendações
//Get a neighborhood of users 
UserNeighborhood neighborhood =         
        new NearestNUserNeighborhood(neighborhoodSize, userSimilarity, dataModel); 
//Create the recommender 
Recommender recommender =         
        new GenericUserBasedRecommender(dataModel, neighborhood, userSimilarity); 
User user = dataModel.getUser(userId); 
System.out.println("-----"); 
System.out.println("User: " + user); 
//Print out the users own preferences first 
TasteUtils.printPreferences(user, handler.map); 
//Get the top 5 recommendations 
List<RecommendedItem> recommendations =         
        recommender.recommend(userId, 5); 
TasteUtils.printRecs(recommendations, handler.map);

É possível realizar o exemplo total na linha de comando, executando ant user-demo no diretório que contém a amostra. Executar este comando imprime as preferências e recomendações para o usuário mítico 995, que tem interesse por Lincoln. A Listagem 3 apresenta o resultado da execução de ant user-demo:

Listagem 3. Resultado da recomendação de usuário
[echo] Getting similar items for user: 995 with a neighborhood of 5
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Creating FileDataModel
 for file src/main/resources/recommendations.txt
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Reading file info...
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Processed 100000 lines
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Read lines: 111901
     [java] Data Model: Users: 1000 Items: 2284
     [java] -----
     [java] User: 995
     [java] Title: August 21 Rating: 3.930000066757202
     [java] Title: April Rating: 2.203000068664551
     [java] Title: April 11 Rating: 4.230000019073486
     [java] Title: Battle of Gettysburg Rating: 5.0
     [java] Title: Abraham Lincoln Rating: 4.739999771118164
     [java] Title: History of The Church of Jesus Christ of Latter-day Saints
 Rating: 3.430000066757202
     [java] Title: Boston Corbett Rating: 2.009999990463257
     [java] Title: Atlanta, Georgia Rating: 4.429999828338623
     [java] Recommendations:
     [java] Doc Id: 50575 Title: April 10 Score: 4.98
     [java] Doc Id: 134101348 Title: April 26 Score: 4.860541
     [java] Doc Id: 133445748 Title: Folklore of the United States Score: 4.4308662
     [java] Doc Id: 1193764 Title: Brigham Young Score: 4.404066
     [java] Doc Id: 2417937 Title: Andrew Johnson Score: 4.24178

A partir dos resultados na Listagem 3, é possível visualizar que o sistema recomendou diversos artigos com vários níveis de confiança. Na verdade, cada um desses itens foi classificado por outros interessados por Lincoln, porém não pelo usuário 995. Caso queira visualizar os resultados para outros usuários, apenas passe no parâmetro -Duser.id=USER-ID na linha de comando, onde USER-ID é um número entre 0 e 999. Também é possível alterar o tamanho da vizinhança, passando em -Dneighbor.size=X, onde X é um inteiro maior do que 0. Na verdade, alterar o tamanho da vizinhança para 10 produz resultados muito distintos, que são influenciados pelo fato de que um dos usuários aleatórios encontra-se na vizinhança. Para visualizar a vizinhança de usuários e os itens em comum, adicione -Dcommon=true à linha de comando.

Agora, se for inserido um número que não faz parte da faixa de usuários, pode-se perceber que o exemplo informa uma NoSuchUserException. De fato, sua aplicação precisaria saber o que fazer quando um novo usuário entra no sistema. Por exemplo, é possível mostrar apenas os 10 artigos mais populares, uma seleção aleatória de artigos, ou uma seleção de artigos "heterogêneos" — ou, até o ponto que interessa, absolutamente nada.

Conforme mencionei anteriormente, a abordagem baseada em usuário, geralmente não é escalável. Nesse caso, é melhor utilizar uma abordagem baseada em item para item. Felizmente, o Taste faz com que o uso de uma abordagem de item para item seja fácil. O código básico para colocar em operação similaridade de item para item não é muito diferente, como pode ser visto na Listagem 4:

Listagem 4. Exemplo de similaridade item-item (de cf.wikipedia.WikipediaTasteItemItemDemo)
//create the data model 
FileDataModel dataModel = new FileDataModel(new File(recsFile)); 
//Create an ItemSimilarity 
ItemSimilarity itemSimilarity = new LogLikelihoodSimilarity(dataModel); 
//Create an Item Based Recommender 
ItemBasedRecommender recommender =         
        new GenericItemBasedRecommender(dataModel, itemSimilarity); 
//Get the recommendations 
List<RecommendedItem> recommendations =         
        recommender.recommend(userId, 5); 
TasteUtils.printRecs(recommendations, handler.map);

Assim como na Listagem 1, eu crio um DataModel a partir do arquivo de recomendações, mas nesta vez, em vez de instanciar uma instância UserSimilarity, eu crio um ItemSimilarity usando o LogLikelihoodSimilarity, que ajuda a lidar com eventos raros. Depois disso, eu forneço o ItemSimilarity a um ItemBasedRecommender e a seguir solicito as recomendações. É isso! É possível executar isto no código de amostra através do comando ant item-demo. A partir daqui, obviamente, é possível definir o sistema até fazer esses cálculos offline, e também explorar outras medidas de ItemSimilarity. Observe que, devido à aleatoriedade dos dados neste exemplo, as recomendações podem não ser conforme esperadas. Na verdade, é importante ter certeza de avaliar seus resultados durante o teste e tentar diferentes métricas de similaridade, já que muitas das métricas comuns possuem certos casos de extremidades que se rompem quando estão disponíveis dados insuficientes para dar recomendações adequadas.

Revisitando o exemplo do usuário novo, o problema de o que fazer na ausência de preferências do usuário torna-se bem mais fácil de lidar uma vez que o usuário navega até um item. Neste caso, é possível aproveitar os cálculos item-item e solicitar ao ItemBasedRecommender os itens que são mais similares ao item atual. A Listagem 5 demonstra isto no código:

Listagem 5. Demo de itens similares (de cf.wikipedia.WikipediaTasteItemRecDemo)
//create the data model 
FileDataModel dataModel = new FileDataModel(new File(recsFile)); 
//Create an ItemSimilarity 
ItemSimilarity itemSimilarity = new LogLikelihoodSimilarity(dataModel); 
//Create an Item Based Recommender 
ItemBasedRecommender recommender =         
        new GenericItemBasedRecommender(dataModel, itemSimilarity); 
//Get the recommendations for the Item 
List<RecommendedItem> simItems         
        = recommender.mostSimilarItems(itemId, numRecs); 
TasteUtils.printRecs(simItems, handler.map);

É possível executar a Listagem 5 a partir da linha de comando executando o ant sim-item-demo. A única diferença real da Listagem 4 é que a Listagem 5, em vez de solicitar recomendações, solicita os itens mais similares ao item de entrada.

A partir daqui, já há o suficiente para pesquisar com Taste. Para saber mais, consulte a documentação do Taste e a lista de e-mails mahout-user@lucene.apache.org (consulte Resources). A seguir, darei uma olhada em como encontrar artigos similares aproveitando algumas das capacidades de armazenamento em cluster do Mahout.


Armazenando em cluster com Mahout

O Mahout suporta diversas implementações de algoritmo de armazenamento em cluster,, todas escritas em Map-Reduce, cada qual com seu próprio conjunto de metas e critérios:

  • Canopy: Um rápido algoritmo de armazenamento em cluster usado para criar sementes iniciais para outros algoritmos de armazenamento em cluster.
  • k-Means (e fuzzy k-Means): Armazena itens nos clusters k com base na distância entre os itens e o centroide, ou centro, da iteração anterior.
  • Mean-Shift: Algoritmo que não exige nenhum conhecimento a priori sobre o número de clusters e pode produzir clusters com formato arbitrário.
  • Dirichlet: Clusters baseados na mistura de muitos modelos probabilísticos dando a ela a vantagem de que não precisa comprometer-se com uma visão específica de clusters prematuramente.

De um ponto de vista prático, os nomes e implementações não são tão importantes como os resultados que produzem. Com isso em mente, mostrarei como funciona o k-Means e deixarei os outros para vocês explorarem. Tenham em mente que cada algoritmo tem suas próprias necessidades para fazê-lo executar de forma eficiente.

Em termos gerais (com mais detalhes a seguir), os passos envolvidos no armazenamento dos dados em clusters usando o Mahout são:

  1. Preparar a entrada. Se estiver armazenando texto em clusters, é necessário converter o texto em uma representação numérica.
  2. Execute o algoritmo de armazenamento em cluster preferido usando um dos muitos programas drivers compatíveis com Hadoop disponíveis no Mahout.
  3. Avalie resultados.
  4. Repita se necessário.

Primeiro e mais importante, os algoritmos de armazenamento em cluster exigem dados que estejam em um formato adequado para processamento. No aprendizado por máquina, os dados são frequentemente representados como um vetor, algumas vezes denominado vetor de recurso. No armazenamento em cluster, um vetor é um array que representa os dados. Demonstrarei o armazenamento em cluster usando vetores produzidos a partir de documentos da Wikipedia, mas os vetores podem vir de outras áreas, tais como dados de sensores ou perfis de usuário. O Mahout vem com duas representações de Vetor: DenseVector e SparseVector. Dependendo de seus dados, será necessário escolher uma implementação apropriada para obter bom desempenho. De modo geral, os problemas baseados em texto são esparsos, tornando o SparseVector a opção certa para eles. Por outro lado, se a maioria dos valores para a maioria dos vetores for diferente de zero, então um DenseVector é mais apropriado. Se estiver inseguro, tente ambos e veja qual deles trabalha mais rápido em um subconjunto de seus dados.

Para produzir vetores a partir do conteúdo da Wikipedia (que eu fiz para vocês):

  1. Indexe o conteúdo no Lucene, não deixando de armazenar os vetores de termo para o campo a partir do qual deseja gerar vetores. Não vou cobrir os detalhes disto — está fora do escopo do artigo — mas fornecerei algumas rápidas dicas juntamente com algumas referências sobre o Lucene. O Lucene vem com uma classe denominada EnWikiDocMaker (no pacote contrib/benchmark do Lucene) que pode ler em uma descarga de arquivo da Wikipedia e produzir documentos para indexação no Lucene.
  2. Crie vetores a partir do índice do Lucene usando a classe org.apache.mahout.utils.vectors.lucene.Driver localizada no módulo utils do Mahout. Este programa driver vem com várias opções para criar vetores. A página wiki do Mahout intitulada Criando Vetores a partir de Texto tem mais informações (consulte Resources).

Os resultados da execução destes dois passos em um arquivo como o n2.tar.gz baixado na seção Introdução ao Mahout. Por motivo de integridade, o arquivo n2.tar.gz compreende vetores criados a partir da indexação de todos os documentos no arquivo de "chunks" da Wikipedia que foi automaticamente baixado pelo método ant install anteriormente. Os vetores foram normalizados usando a norma Euclideana (ou norma L2 ; consulte Resources). No uso do Mahout, provavelmente o usuário vá desejar tentar criar vetores em diversas formas para ver qual deles produz os melhores resultados.

Avaliando os resultados

Há muitas abordagens à avaliação dos resultados de cluster. Muitas pessoas começam simplesmente usando inspeção manual e teste ad-hoc. Entretanto, para ficar verdadeiramente satisfeito, é muitas vezes necessário usar técnicas de avaliação mais detalhadas tais como o desenvolvimento de um padrão de ouro usando diversos juízes. Para saber mais sobre a avaliação de seus resultados, consulte Resources. Para meus exemplos, usei a inspeção manual para ver se alguns dos resultados que foram armazenados em cluster em conjunto realmente faziam sentido. Se eu fosse colocar isso em produção, I usaria um processo muito mais rigoroso.

Dado um conjunto de vetores, o próximo passo é executar o algoritmo de armazenamento em cluster k-Means. O Mahout fornece programas drivers para todos os algoritmos de armazenamento em cluster, incluindo o algoritmo k-Means, coerentemente denominado KMeansDriver. O driver é de fácil uso como programa individual sem o Hadoop, como demonstrado executando o ant k-means. Fique à vontade para examinar o Ant k-means alvo no build.xml para mais informações sobre os argumentos que o KMeansDriver aceita. Após o final do processo, é possível imprimir os resultados usando o comando ant dump.

Após ter executado com sucesso no modo individual, é possível proceder para executar no modo distribuído no Hadoop. Para isso, será necessário o Mahout Job JAR, que está localizado no diretório hadoop no código da amostra. Um Job JAR comprime todos os códigos e dependências em um único arquivo JAR para uma fácil carga no Hadoop. Será necessário também baixar o Hadoop 0.20 e seguir as instruções sobre o tutorial Hadoop para executar primeiro no modo pseudo-distribuído (ou seja, um cluster de um) e a seguir totalmente distribuído. Para maiores informações, vide o Web site e recursos Hadoop, assim como os recursos de computação em nuvem da IBM (consulte Resources).


Categorização de conteúdo com Mahout

O Mahout atualmente suporta duas abordagens relacionadas para categorizar/classificar o conteúdo com base em estatísticas bayesianas. A primeira abordagem é um simples classificador de Naive Bayes habilitado para Map Reduce. Os classificadores Naive Bayes são conhecidos por serem rápidos e bem precisos, apesar de suas suposições muito simples (e frequentemente incorretas) sobre os dados serem completamente independentes. Os classificadores Naive Bayes frequentemente se dividem quando o tamanho dos exemplos de treinamento por classe não é balanceado ou quando os dados não são independentes o bastante. A segunda abordagem, denominada Naive Bayes Complementar, tenta corrigir alguns dos problemas com a abordagem de Naive Bayes, mantendo sua simplicidade e velocidade. Entretanto, para este artigo, mostrarei apenas a abordagem de Naive Bayes, porque ela demonstra o problema geral e entradas no Mahout.

Em resumo, um classificador Naive Bayes é um processo de duas partes que envolve rastrear os recursos (palavras) relacionados a um documento específico e categoria e a seguir usar essas informações para prever a categoria de conteúdo novo inédito. O primeiro passo, denominado treinamento, cria um modelo olhando exemplos de conteúdo já classificado e a seguir rastreia as probabilidades de que cada palavra esteja associada a um conteúdo específico. O segundo passo, denominado classificação, usa um modelo criado durante o treinamento e o conteúdo de um documento novo, inédito, juntamente com o Teorema de Bayes, para prever a categoria do documento passado. Assim, para executar o classificador do Mahout, é necessário primeiro treinar o modelo e a seguir usar esse modelo para classificar novo conteúdo. A próxima seção demonstrará como fazer isso usando o conjunto de dados do Wikipedia.

Execução do classificador Naive Bayes

Antes de executar o treinador e classificador, é necessário realizar um pequeno trabalho de preparo para configurar um conjunto de documentos para treinamento de um conjunto de documentos para teste. É possível preparar os arquivos da Wikipedia (dentre aqueles baixados via meta de install) executando ant prepare-docs. Isto divide os arquivos de entrada da Wikipedia usando a classe WikipediaDatasetCreatorDriver incluso nos exemplos do Mahout. Os documentos são divididos com base do fato de o documento ter ou não uma categoria que corresponda a uma das categorias de interesse. As categorias de interesse podem ser qualquer categoria válida da Wikipedia (ou mesmo qualquer substring de uma categoria da Wikipedia). Por exemplo, neste caso, incluí duas categorias: Ciência e História. Assim, qualquer categoria da Wikipedia que tiver uma categoria contendo a palavra ciência ou história (não precisa ser um correspondente exato) será colocada em conjunto com outros documentos para essa categoria. Além disso, cada documento é marcado e normalizado para remover a pontuação, a marcação da Wikipedia, e outros recursos que não são necessários para esta tarefa. Os resultados finais são armazenados em um único arquivo rotulado com o nome da categoria, um documento por linha, que é o formato de entrada que o Mahout espera. Da mesma forma, executar o ant prepare-test-docs faz o mesmo trabalho para os documentos de teste. É importante que os documentos de teste e treinamento não se sobreponham, o que poderia distorcer os resultados. Na teoria, usar os documentos de treinamento para teste resultaria em resultados perfeitos, mas mesmo isso não é provável na prática.

Após os conjuntos de treinamento e teste serem definidos, é hora de executar a classe TrainClassifier via alvo ant train. Isto resultaria em uma grande quantidade de registros tanto do Mahout quanto do Hadoop. Uma vez concluído, o ant test toma os documentos de teste de amostra e tenta classificá-los usando o modelo que foi construído durante o treinamento. O resultado desse teste no Mahout é uma estrutura de dados denominada matriz de confusão. Uma matriz de confusão descreve quantos resultados foram corretamente classificados e quantos foram incorretamente classificados para cada uma das categorias.

Em resumo, você executa os seguintes passos para produzir resultados de classificação:

  1. ant prepare-docs
  2. ant prepare-test-docs
  3. ant train
  4. ant test

Executar todos eles (o Ant target classifier-example captura todos eles em uma chamada), resultando no resumo e na matriz de confusão mostrados na Listagem 6:

Listagem 6. Resultados da execução do classificador Bayes para história e ciência
[java] 09/07/22 18:10:45 INFO bayes.TestClassifier: history
                                  95.458984375    3910/4096.0
[java] 09/07/22 18:10:46 INFO bayes.TestClassifier: science
                                  15.554072096128172      233/1498.0
[java] 09/07/22 18:10:46 INFO bayes.TestClassifier: =================
[java] Summary
[java] -------------------------------------------------------
[java] Correctly Classified Instances          :       4143
                                                    74.0615%
[java] Incorrectly Classified Instances        :       1451
                                                    25.9385%
[java] Total Classified Instances              :       5594
[java]
[java] =======================================================
[java] Confusion Matrix
[java] -------------------------------------------------------
[java] a           b       <--Classified as
[java] 3910        186      |  4096        a     = history
[java] 1265        233      |  1498        b     = science
[java] Default Category: unknown: 2

Os resultados dos processos intermediários são armazenados no diretório com o nome wikipedia no diretório base.

Com um conjunto de resultados em mãos, a pergunta óbvia é: "Como fiz isso?" O resumo indica que consegui cerca de 75% correto e 25% incorreto. À primeira vista, isso parece bem razoável, especialmente pois significa que fiz melhor que adivinhar aleatoriamente. Um exame mais apurado mostra, contudo, que realmente me saí muito bem ao prever a história (aproximadamente 95% corretamente) e muito mal ao prever a ciência (aproximadamente 15%). Ao observar os motivos, uma rápida olhada nos arquivos de entrada para treinamento demonstra que tenho muito mais exemplos de história do que ciência (o tamanho do arquivo é quase o dobro), o que é provavelmente um problema potencial.

Para o teste, é possível adicionar a opção -Dverbose=true para ant test, que apresenta informações sobre cada entrada de teste e se foi corretamente rotulado ou não. Analisando este resultado, é possível buscar documentos e examiná-los para obter conclusões sobre a causa de poder ter sido classificado incorretamente. Também posso tentar diferentes parâmetros de entrada e, também, mais dados de ciência e retreinar o modelo para ver se consigo melhorar os resultados.

Também é importante considerar sobre a seleção de recursos para treinamento do modelo. Para esses exemplos, utilizei o WikipediaTokenizer do Apache Lucene para tokenizar os documentos originais, mas não fiz muito esforço para remover termos comuns ou termos desnecessários que poderiam ter sido tokenizados incorretamente. Se estivesse buscando colocar este classificador em produção, faria um exame mais profundo das entradas e outras configurações, tentando extrair o máximo desempenho.

Apenas para ver se os resultados de Ciência eram um golpe de sorte, tentei um conjunto diferente de categorias: Republicanos e Democratas. Neste caso, quero prever se um novo documento é sobre Republicanos ou Democratas. Para permitir que você tente por sua própria conta, criei o repubs-dems.txt em src/test/resources. A seguir, executei os passos de classificação via:

ant classifier-example -Dcategories.file=./src/test/resources/repubs-dems.txt -Dcat.dir=rd

Os dois valores -D simplesmente apontam para o arquivo de categoria e o nome do diretório para colocar os resultados intermediários no diretório wikipedia. O resumo e a matriz de confusão desta execução são conforme a Listagem 7:

Listagem 7. Resultados da execução do classificador Bayes para Republicanos e Democratas
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: -------------- 
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: Testing:
                                 wikipedia/rd/prepared-test/democrats.txt 
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: democrats      70.0
                                                                    21/30.0 
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: -------------- 
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: Testing:
                               wikipedia/rd/prepared-test/republicans.txt 
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: republicans    81.3953488372093
                                                                    35/43.0  
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: 
 [java] Summary 
 [java] ------------------------------------------------------- 
 [java] Correctly Classified Instances          :         56           76.7123% 
 [java] Incorrectly Classified Instances        :         17           23.2877% 
 [java] Total Classified Instances              :         73  
 [java] 
 [java] ======================================================= 
 [java] Confusion Matrix 
 [java] ------------------------------------------------------- 
 [java] a           b       <--Classified as 
 [java] 21          9        |  30          a     = democrats 
 [java] 8           35       |  43          b     = republicans 
 [java] Default Category: unknown: 2

Embora o resultado final seja quase o mesmo em termos de correção, é possível ver que fiz um trabalho melhor de decidir entre as duas categorias. Um rápido exame no diretório wikipedia/rd/prepared que contém os documentos de entrada mostra que os dois arquivos de treinamento eram muito mais equilibrado em termos de exemplos de treinamento. O exame também mostra que possuo bem menos exemplos de modo geral em comparação à execução de história/ciência, pois cada arquivo é muito menor que o do conjunto de treinamento de história ou ciência. De modo geral, os resultados, pelo menos, parecem ser bem mais equilibrados. Conjuntos de treinamento maiores provavelmente equilibrariam as diferenças em Republicanos e Democratas, embora se não o fizesse, isso poderia implicar que um grupo é melhor em aderir à sua mensagem na Wikipedia —, porém deixarei isso para os gurus políticos decidirem.

Agora, que demonstrei como executar a classificação em modo individual, os próximos passos devem ser levar o código à nuvem e executar em um cluster Hadoop. Da mesma forma que o código de armazenamento em cluster, será necessário o Mahout Job JAR. Além disso, todos os algoritmos que mencionei anteriormente são compatíveis com Map-Reduce e devem operar somente ao executar no processo de submissão de Trabalho descrito no tutorial Hadoop.


O que vem a seguir para o Mahout?

O Apache Mahout chegou longe em apenas pouco mais de um ano, com capacidades significativas para armazenamento em cluster, categorização e CF, mas também possui muito espaço para crescer. No horizonte imediato estão as implementações Map-Reduce de florestas de decisão aleatória para classificação, regras de associação, Latent Dirichlet Allocation para a identificação de tópicos em documentos e mais opções de categorização usando HBase e outras opções de armazenamento de suporte. Além dessas novas implementações, procure por mais demos, maior documentação, e correções de bugs.

Finalmente, assim como um mahout real impulsiona a força e as capacidades do elefante, o Apache Mahout também pode ajudá-lo a impulsionar a força e as capacidades do elefante amarelo que é o Apache Hadoop. Na próxima vez que precisar armazenar em cluster, categorizar ou recomendar conteúdo, especialmente em grande escala, dê uma olhada no Apache Mahout.

Agradecimentos

Agradecimento especial aos colegas do comitê Mahout, Ted Dunning e Sean Owen por sua revisão e insights neste artigo.


Download

DescriçãoNomeTamanho
Sample codej-mahout.zip90MB

Recursos

Aprender

Obter produtos e tecnologias

  • Download Hadoop 0.20.0 (a página reside fora da ibm.com).
  • Download um subconjunto da Wikipedia (a página reside fora da ibm.com).
  • Obtenha dados de classificação de filme do projeto GroupLens (a página reside fora da ibm.com).

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Software livre
ArticleID=432430
ArticleTitle=Introdução ao Apache Mahout
publish-date=09252009