Back-End

1 mar, 2019

Sistema de recomendação em Java

Publicidade

O Python domina a área de inteligência artificial, mas sabia que com Java também é possível fazer algumas coisas?

Neste artigo irei abordar alguns conceitos e faremos uma aplicação bem simples de recomendação de livros e filmes.

Primeiramente, você sabe o que é Machine Learning?

Se trata do aprendizado de máquina, um ramo da IA (inteligência Artificial) que se baseia na ideia de que sistemas são capazes de aprender com dados, identificar padrões e realizar tomadas de decisões com o mínimo de intervenção de um ser humano.

E o que são sistemas de recomendação?

São técnicas de software que fornecem sugestões de itens para ajudar usuários deste sistema nos diversos processos de tomada de decisão.

Seria como um vendedor que só com alguns minutos de conversa já sabe qual produto é o ideal para o seu cliente.

Utilizaremos o algoritmo Person Correlation Similarity, que se baseia na similaridade entre pessoas na hora de escolher e atribuir notas para determinar os resultados de sua recomendação.

E aí, bora codar?

Antes de tudo, verifique se já tem ou instale o Eclipse (versão Java EE Developers).

Se for sua primeira vez utilizando o Maven, que é um gerenciador de dependências, deverá fazer download do mesmo (descompactar) e executar os seguintes passos:

Configuração incial do maven

No diretório em que deseja criar o projeto, execute o seguinte comando (no terminal):

mvn archetype:generate -DgroupId=br.com.kamila -DartifactId=sistema-de-recomendacao-java-mahout -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Onde:

  • mvn archetype:generate cria projeto
  • GroupId: identificação do grupo do projeto – por convenção inciamos com nomedopais.com.nomedaorganização
  • ArtifactId: nome do projeto
  • arc hetypeArtifactId: tipo de configuração inicial do projeto. No caso, o quickstart usa configurações default

Se o seu prompt não reconhecer o comando mvn, devemos adicioná-lo no PATH:

PATH=$PATH:onde_esta_o_bin_do_maven

E tente executar o comando para gerar o projeto novamente:

Caso já tenha o Maven configurado, basta criar um novo Maven project do tipo QuickStart.

Agora vamos realizar a seguinte configuração: dentro do arquivo pom.xml estão as nossas dependências – vamos adicionar o Mahout e o Hadoop client. Seu arquivo deverá estar parecido com este (os comentários são para facilitar a compreensão do que foi utilizado em cada parte do projeto):

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>br.com.kamila</groupId>
  <artifactId>sistema-de-recomendacao-java-mahout</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>sistema-de-recomendacao-java-mahout</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!--mahout cuida da parte dos algoritmos de similaridade !-->
    <dependency>
    	<groupId>org.apache.mahout</groupId>
    	<artifactId>mahout-mr</artifactId>
    	<version>0.12.2</version>
    </dependency>
    <!--hadoop faz a análise dos nossos dados !-->
    <dependency>
    	<groupId>org.apache.hadoop</groupId>
    	<artifactId>hadoop-client</artifactId>
    	<version>2.7.2</version>
    </dependency>
  </dependencies>
</project>

Dentro da pasta src\main\java\br\com\(nome da sua organização) criaremos o arquivo Recomendador.java, que guarda os modelos de dados que serão analisados:

package br.com.kamila;

import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.model.DataModel;

import java.io.File;
import java.io.IOException;
//nesta classe está a relação dos modelos de filmes e livros para a serem recomendados
public class Recomendador {

    public DataModel getModeloLivros() throws IOException {
        //arquivo usado com a base de usuários, os códigos dos livros e as notas atribuídas
        return getModelo("livros.csv");
    }

    private DataModel getModelo(String path) throws IOException {
        File file = new File("src/main/resources/" + path);
        return new FileDataModel(file);
    }

    public DataModel getModeloDeFilmes() throws IOException {
        //arquivo usado com a base de usuários, os códigos dos filmes e as notas atribuídas
       return getModelo("filmes.csv");
    }

}

Os arquivos livros.csv e filmes.csv você pode acessar neste link: https://github.com/Kamilahsantos/sistema-de-recomendacao-java-mahout/tree/master/src/main/resources

Ainda nesta pasta vamos criar o arquivo RecomendadorBuilder.java, que, como o nome já diz, constrói a lógica para o nosso recomendador:

package br.com.kamila;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
import org.apache.mahout.cf.taste.impl.neighborhood.ThresholdUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.recommender.UserBasedRecommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
//aqui connstruimos nosso recomendador
public class RecomendadorBuilder implements RecommenderBuilder {
    public Recommender buildRecommender(DataModel model) throws TasteException {
        //ele irá se basear na similaridade entre os usuários para contruir recomendações.
        UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
        //pega a maior similaridade (vizinhança) entre os usuários do nosso modelo
        UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, model);
        //fazemos nossa recomendação baseada no usuário, que é retornada no recommender 
        UserBasedRecommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
        return recommender;
    }
}

Vamos recomendar filmes para os nossos usuários? Vamos criar o arquivo RecomendaFilmes.java:

package br.com.kamila;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;

import java.io.IOException;
import java.util.List;

public class RecomendaFilmes {
    public static void main(String[] args) throws IOException, TasteException {
        DataModel filmes = new Recomendador().getModeloDeFilmes();
        Recommender recommender = new RecomendadorBuilder().buildRecommender(filmes);
        //listamos as recomedações para o usuário solicitado
        //TODO receber as informações de usuário e número de recomendações do usuário via teclado
        //primeiro parâmentro e usuário , segundo o número de recomendações
        List<RecommendedItem> recommendations = recommender.recommend(4, 3);
        for (RecommendedItem recommendation : recommendations) {
            System.out.println("Voce pode gostar deste filme");
            System.out.println(recommendation);
        }
    }
}

Também vamos recomendar livros. Crie o arquivo RecomendaLivros.java:

package br.com.kamila;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;

import java.io.IOException;
import java.util.List;

public class RecomendaLivros {

	public static void main(String[] args) throws TasteException, IOException {
		DataModel produtos = new Recomendador().getModeloLivros();
        Recommender recommender = new RecomendadorBuilder().buildRecommender(produtos);
        //listamos as recomedações para o usuário solicitado
        System.out.println("usuario 1");
        List<RecommendedItem> recommendations = recommender.recommend(1, 4);
        for (RecommendedItem recommendation : recommendations) {
            System.out.println("usuário 1, estes livros combinam com voce");
            System.out.println(recommendation);
        }

        System.out.println("usuario 2");
        recommendations = recommender.recommend(2, 4);
        for (RecommendedItem recommendation : recommendations) {
            System.out.println("usuário 2, estes livros combinam com voce");
            System.out.println(recommendation);
        }       
  }
}

Para avaliar a margem de erro do nosso recomendador, criamos o arquivo Avaliador.java:

package br.com.kamila;
//realizando todos os imports necessários:
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
import org.apache.mahout.cf.taste.eval.RecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.common.RandomUtils;

import java.io.IOException;
//esta classe avaliador mede a taxa de erro do  nosso recomendador
public class Avaliador {
    public static void main(String[] args) throws IOException, TasteException {
        //o método usetestseed permite fixar valores para teste, impedindo resultados aleatórios a cada teste
        RandomUtils.useTestSeed();
        //usa  o modelo de de análise
        DataModel modelo = new Recomendador().getModeloDeFilmes();
        //calcula a média absoluta dos testes para informar a taxa de erro
        RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
        RecommenderBuilder builder = new RecomendadorBuilder();
        //90% para treino e 10% para teste
        double erro = evaluator.evaluate(builder, null, modelo, 0.9, 1.0);
        System.out.println("A taxa de erro desse sistema de recomendacao atualmente e:");
        System.out.println(erro);

    }
}

Agora é só executar. Escolha se quer executar o Avaliador, RecomendaLivros ou RecomendaFilmes.

Se, ao executar, exibir um erro referente ao log4j, devemos criar o arquivo log4j.xml com este conteúdo:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="main" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%t] %d{HH:mm:ss} %-5p %l-%m%n"/>
        </layout>
    </appender>
    <category name="org.apache">
        <priority value="DEBUG"/>
        <appender-ref ref="main"/>
    </category>

</log4j:configuration>

Alguma dúvida ou sugestão?

Deixo o repositório com o código e as referências utilizadas para construir este artigo: