Coletânea Python do ZERO às Redes Neurais Artificiais

Fernando Feltrin · Capítulo 268 de 287

Páginas do PDF

Coletânea Python do ZERO às Redes Neurais Artificiais

Regressão com Múltiplas Saídas – VGDB Dataset

Uma das aplicações bastante comuns em ciência de dados é tentarmos estipular números de vendas de um determinado produto, para a partir disso gerar algum prospecto de mudanças em seu modelo de vendas. Aqui, para entendermos esse tipo de conceito de forma prática, usaremos uma base de dados de vendas de jogos de videogame onde temos as informações de suas vendas em três mercados diferentes. Temos dados brutos das vendas dos jogos na américa do norte, na europa e no japão, após o devido polimento e processamento dos dados dessa base, poderemos ver qual região obteve maior lucro em suas vendas para assim planejar estratégias de vendas nas outras de menor lucro. Para isto estaremos usando uma base de dados real disponível gratuitamente no repositório Kaggle.

 

Dentro do site do Kaggle, basta procurar por Video Game Sales e da página do dataset fazer o download do respectivo arquivo em formato .csv.

 

Partindo para o código:

Inicialmente criamos um novo arquivo Python 3 de nome VGDB.py, em seguida, como sempre fazemos, realizamos as devidas importações das bibliotecas, módulos e ferramentas que nos auxiliarão ao longo do código. Repare que aqui temos uma pequena diferença em relação aos exemplos anteriores, não importamos Sequential (módulo que criava e linkava automaticamente as camadas de nossa rede neural) e importamos o módulo Model, que por sua vez nos permitirá criar uma rede neural densa com múltiplas saídas.

 

Em seguida criamos uma variável de nome base que recebe por meio da função .read_csv( ) o conteúdo de nosso arquivo games.csv parametrizado aqui.

 

Uma vez realizada a importação é possível via explorador de variáveis ver que de fato foi criada nossa variável base em forma de DataFrame, onde inicialmente temos 16719 objetos/amostras/registros com atributos divididos em 16 categorias diferentes.

Analisando o conteúdo de base, podemos ver que, como de costume, temos muitas inconsistências nos dados, muitos dados faltando e por fim, colunas de informação desnecessárias para nosso propósito. Sendo assim iniciamos a fase de polimento dos dados removendo as colunas desnecessárias.

Diretamente sobre nossa variável base aplicaremos algumas funções. Primeiramente por meio da função .drop( ) eliminamos as colunas Other_Sales, Global_Sales e Developer, note que para eliminar colunas de nossa base devemos colocar como parâmetro também axis = 1.

Na sequência por meio da função .dropna( ) excluímos todas as linhas onde haviam valores faltando (não zerados, faltando mesmo), e agora como estamos aplicando função sobre linhas, agora axis = 0. Por fim, por meio da expressão .loc[base[‘NA_Sales’] > 1] mantemos dessa coluna apenas as linhas com valores maiores que 1, o mesmo é feito para EU_ Sales.

 

Repare que após a aplicação das funções anteriores, houve uma redução considerável no número de amostras que temos. O que indica que dentro dos parâmetros que definimos haviam muitas inconsistências de dados, e isto é perfeitamente normal quando pegarmos bases de dados geradas tanto por sistemas quanto por usuários, o importante é que dos poucos dados que sobrem, estes sejam íntegros para que possamos processá-los e extrair informações a partir dos mesmos.

 

Dando sequência realizaremos um procedimento de extrair os dados de uma coluna inteira de nossa base, porém não iremos descartar tais dados, apenas separá-los da base salvando-os em uma nova variável. Para isso aplicando sobre a coluna Name de nossa base a função .value_counts( ) fazemos a leitura de todos os dados/valores que houverem nessa coluna.

Em seguida criamos uma variável de nome backupBase que recebe os dados contidos em base.Names. Por fim, por meio da função .drop( ) eliminamos de nossa base a coluna inteira Name.

Eliminadas as inconsistências de nossa base de dados, hora de dividir a mesma em dados de entrada e de saída. Inicialmente criamos uma variável de nome entradas que recebe como atributo todos os dados das colunas 0, 1, 2, 3, 7, 8, 9, 10 e 11 por meio de .iloc[ ].values.

Na sequência criamos 3 variáveis de saída, uma vez que neste exemplo é isto que queremos. A variável vendas_NA recebe os dados de todas as linhas da coluna 4, vendas_EU recebe o conteúdo de todas as linhas da coluna 5 e por fim vendas_JP recebe todas as linhas da coluna 6.

 

Se não houver nenhum erro de sintaxe nesse processo podemos ver que as devidas variáveis foram criadas. Note que temos dados numéricos e em forma de texto, sendo assim, próxima etapa da fase de polimento é converter todos dados para tipo numérico para que possam ser processados pela rede.

 

Como sempre, realizamos as devidas importações das ferramentas que inicialmente não são pré-carregadas por nossa IDE.

Dando sequência criamos nossa variável labelencoder que inicializa a ferramenta LabelEncoder, sem parâmetros mesmo. Na sequência é aplicada a função .fit_transform( ) sobre as colunas 0, 2, 3 e 8, transformando seus dados do tipo string para int.

 

Próximo passo é tornar esse conteúdo convertido indexado de forma que nosso interpretador consiga fazer a correta leitura dos dados e aplicar funções sobre os mesmos. Logo, criamos nossa variável de nome onehotencoder que executa a ferramenta OneHotEncoder e seu parâmetro categorical_features sobre as colunas 0, 2, 3 e 8. Por fim, nossa variável entradas recebe a conversão de tais dados para uma array do tipo numpy por meio das funções .fit_transform( ).toarray( ).

 

Finalmente terminada a fase de polimento dos dados, damos início a criação da estrutura de nossa rede neural artificial. Nos modelos anteriores estivemos criando redes neurais sequenciais.

Aqui criaremos um modelo um pouco diferente, ainda sequencial mas iremos criar os laços entre as camadas de forma totalmente manual, por fim, a segunda grande diferença é que estaremos criando um modelo de rede que processará uma base de dados para gerar múltiplas saídas independentes, uma vez que aqui estamos trabalhando para descobrir preços de vendas em 3 locações diferentes.

Inicialmente criamos nossa variável camada_entrada que recebe como atributo Input que por sua vez recebe o formado da array gerada anteriormente, note que não estamos pegando as amostras e transformando em neurônios da camada de entrada, as amostras por sua vez foram convertidas e indexadas como parte de uma matriz.

Em seguida criamos uma variável de nome camada_oculta1 que recebe como atributo Dense que por sua vez tem como parâmetros units = 32 e activation = ‘sigmoid’, em outras palavras, nossa rede que tem 61 neurônios em sua camada de entrada, agora tem 32 em sua primeira camada oculta, e o processamento desses neurônios com seus respectivos pesos deve gerar uma probabilidade entre 0 e 1 por causa da função de ativação escolhida ser a sigmoide.

Por fim, para haver a comunicação entre uma camada e outra a referência da camada é passada como um segundo parâmetro independente. Para finalizar são criadas 3 camadas de saída, cada uma com 1 neurônio, activation = linear (ou seja, não é aplicada nenhuma função, apenas replicado o valor encontrado) e as devidas linkagens com camada_oculta2.

 

Em seguida é criado nosso regressor, por meio da variável homônima, que recebe como atributo Model que por sua vez tem como parâmetros inputs = camada_entrada e outputs = [camada_saida1, camada_saida2, camada_saida3].

Logo após é executado sobre nosso regressor a função .compile que define o modo de compilação do mesmo, optimizer = ‘adam’ e loss = ‘mse’. Adam já é um otimizador conhecido nosso de outros modelos, mas neste caso ‘mse’ significa mean squared error, é um modelo de função de perda onde o resultado encontrado é elevado ao quadrado e reaplicado na rede, é uma forma de dar mais peso aos erros e forçar a rede a encontrar melhores parâmetros de processamento.

Por fim alimentamos nosso regressor por meio da função .fit( ) com os respectivos dados de entrada e de saída, assim como definimos que a rede será executada 5000 vezes, atualizando os pesos a cada 100 amostras.

Como de costume, é interessante realizar previsões e as devidas comparações para testarmos a eficiência de nossa rede. Para isso criamos as variáveis previsao_NA, previsao_EU e previsao_JP que recebe nosso regressor, aplicando a função .predict( ) diretamente sobre nossos dados de entradas.

 

Por fim podemos equiparar e comparar as colunas dos respectivos nomes dos jogos, os valores reais extraídos de nossa base de dados e os valores encontrados por nossa rede neural artificial, note que de fato os dados são próximos, ou pelo menos possuem uma margem de erro dentro do aceitável. Sendo assim, podemos salvar esse modelo para reutilizar quando for necessário resolver algum tipo de problema parecido.

 

Código Completo: