Deep Learning para Sistemas de Recomendação (Parte 2) — Filtragem Colaborativa com AutoEncoders
Como utilizar arquiteturas de Deep AutoEncoders para recomendar Jogos
Outros artigos da série de Deep Learning e Deep RecSys:
Neste artigo iremos abordar o problema da Recomendação de Conteúdo com Filtragem Colaborativa e como modelar uma solução utilizando uma arquitetura de Deep RecSys com Autoencoders (AE). As códigos foram escritos no Framework Keras e requer um conhecimento básico do assunto. O código completo do projeto pode ser baixado em https://github.com/marlesson/recsys_autoencoders
Os principais tópicos desse artigo são:
- Introdução a Filtragem Colaborativa
- O que são Autoencoders?
- Modelagem do Autoencoder para Filtragem Colaborativa
- Implementação do Modelo em Keras
- Conclusão
1. Introdução a Filtragem Colaborativa
Já apresentamos as principais características da Filtragem Colaborativa na
1º Parte dessa série. Iremos apenas complementar essa introdução aqui.
O princípio da Filtragem Colaborativa é utilizar a informação das interações que ocorrem entre os usuários e os conteúdos para que, de forma coletiva, essa informação seja útil para inferir as preferências dos indivíduos. Dessa forma é possível gerar recomendações como: “usuários que gostaram de Crysis também gostaram de Bioshock Infinite”.
Antes de continuar, precisamos definir e categorizar o que são “interações” ou “preferências” dos usuários nesse contexto, visto que, essa é a principal fonte de informação para os modelos de Filtragem Colaborativa.
Preferências explícitas e implícitas
O usuário do sistema pode interagir com o conteúdo de diversas formas, como dar uma nota, adicionar como preferido, compartilhar, apenas visualizar, visualizar e não interagir, dar play…, etc.
A forma dessa interação depende muito do domínio da recomendação, por exemplo no domínio de notícias, poderia ser quanto tempo ele ficou lendo, se o usuário visualizou, se compartilhou o texto nas redes sociais, etc. Dificilmente o usuário daria uma nota as notícias ou adicionaria aos favoritos, mas no domínio de filmes essas interações seriam mais comuns de realizar.
Essas preferências podem ser categorizadas como explícitas e implícitas. As explícitas são quando o usuário indica espontaneamente o que lhe é importante e/ou o grau dessa importância, um exemplo seria o usuário adicionar o conteúdo aos favoritos ou dar uma nota. As implícitas são coletadas a partir do comportamento do usuário dentro do sistema, essas informações podem indicar as preferências quando não é possível obtê-las de forma explícita, um exemplo poderia ser as matérias visualizadas em um site de notícias, tempo de leitura, porcentagem do vídeo assistido, termos de buscas, etc.
Decidir qual informação coletar como preferência do usuário vai depender do próprio sistema e do domínio da recomendação. É importante ressaltar que, independente da informação que será utilizada como preferência, o objetivo é sempre “modelar o interesse do usuário ao conteúdo”.
A preferência implícita deve modelar o interesse do usuário ao conteúdo
Essa categorização das preferências entre explicita e implícita é importante no contexto de RecSys, pois, em muitos casos o modelo/arquitetura é preferencialmente utilizada em uma das categorias.
2. O que são Autoencoders?
Um Autoencoder (AE) é uma categoria de algoritmos que são formados por duas partes, um encoder e um decoder.
- O encoder tem como função comprimir a informação da entrada em um espaço-latente diferente. Pode ser representado como um função de x, f(x)=h.
- O decoder por sua vez faz o trabalho inverso e reconstroi a informação original, passando do espaço-latente criado pelo encoder para o espaço original da informação. Pode ser representado como uma função de h, g(h) = x’
O AE pode ser descrito como:
É importante ressaltar que o AE aceita perda de informação, ou seja, devido a compressão, o x’ é uma aproximação do x original, x ~ x’ .
Em Deep Learning, os AE são uma das várias categorias de arquiteturas que podem ser utilizadas em diferentes situações, inclusive em Sistemas de Recomendação.
Uma rede do tipo AE é treinada de forma não supervisionada (embora pareça supervisionada), pois não existe ou necessita de um target além das features. Como a saída esperada é a mesma informação que foi entrada na rede, o treinamento do AE é fazer com que a rede aprenda a representar a própria entrada em um espaço-latente (Hidden layers) diferente. A função de custo (loss) do treinamento de um AE está relacionada a diferença entre a entrada e a saída apresentada pela rede, como:
Sobre o tamanho do espaço-latente, quantidade de neurônios utilizada pare representar esse espaço, é importante ressaltar que ele não pode ser pequeno demais a ponto de não ser representativo ou se tornar um index do dado original, ou grande demais a ponto de não existir compressão e o espaço-latente se transformar na própria entrada. Pensar nos casos extremos com o espaço-latente de tamanho 1 e de tamanho igual o da entrada ajuda é entender esses problemas.
Qual a utilidade de um AutoEncoder?
Se você ainda não pensou como uma AE pode ser útil vamos deixar um pouco mais claro as aplicações. A primeira mão os “AutoEncoder” parecem não ter utilidade nenhuma (uma rede que aprende representar a própria entrada), mas elas podem ser utilizadas para diversas funções:
- Redução de Dimensionalidade Geralmente o espaço-latente entre o encoder e o decoder é menor que a entrada, então a rede é capaz de reduzir a dimensionalidade da informação original, semelhando ao processo do PCA, mas aqui com mais liberdade no tipo de dado e capacidade de adicionar não-linearidade nessa compressão.
- Limpeza de ruídos nos dados. Como o processo de compressão/reconstrução garante que apenas o que é “útil” na informação será mantido no espaço-latente, o AE pode ser utilizado como um filtro para remoção de ruidos, limpando pixels de ruidos em imagens ou frequências indesejadas em audio. Veja o DAE.
- Geração de Informação. Assim como as GANs aprendem a gerar informação, existe uma variante do AE que faz o mesmo. Ao aprender a representar a informação em um espaço-latente como uma distribuição de probabilidade, é possível usar essa distribuição para gerar informação totalmente nova, fora do escopo de dados utilizado no treinamento por exemplo. Essa é uma técnica recente e um pouco mais complexa, chamada de VAE, veja uma referência aqui.
Os AE são uma classe de arquiteturas que utilizam a ideia de encoder-decoder e são extremamente dinâmicas quanto a funcionalidade.
No Blog oficial do Keras têm várias implementações das variantes dos AE. Para outras aplicações não usuais dos AE sugiro o material do Colin Raffel, “A Few Unusual Autoencoders” aqui ao lado.
3. Modelagem do AutoEncoder para Filtragem Colaborativa
Agora surge a pergunta:
“como um AutoEncoder pode resolver a Filtragem Colaborativa?”
A resposta é mais simples do que parece. O objetivo do AE é comprimir para depois reconstruir… Então, podemos de fato comprimir a Matriz de Interação característica por ser esparsa e depois reconstruir uma versão completa da Matriz contendo uma aproximação de todos os valores que antes eram nulos. Algo como:
Essa é uma ideia geral, ao fazer um comparativo com os Algorítimos de Fatoração de Matrizes (ALS, SVD..) que são bastante utilizados para resolver a Filtragem Colaborativa, usar um AE garante alguns benefícios:
- A esparsividade da matriz de interação não é um problema, visto que o AE originalmente trabalha bem com reduação de dimensionalidade.
- Interações que podem ser “ruídos” não interferem tanto no resultado final. Um exemplo é sua conta da Steam ser usada pelo seu filho, talvez o jogo “Frozen Free Fall” não seja sua preferência de fato.
- Assim como outras arquiteturas de Deep Learning, os AE são extremamento customisáveis. Então existe a facilidade em adicionar mais informações além das interações que são utilizadas na matriz para criar Métodos Hibridos de RecSys.
- O espaço-latente criado pode ser utilizado como informação em outros métodos de machine learning fora do contexto de RecSys. Um exemplo é buscar usuários similares para utilizar em campanhas de marketing..
Existem outros benefícios em utilizar Deep AutoEncoders para RecSys, essa linha de pesquisa ainda é recente e sempre surgem novidades.
…show me the code…
Problema
Vamos supor que trabalhamos para o Gabe Newell e somos responsáveis por montar a lista de jogos que devem aparecer na home da Loja da Steam. O catálogo da Steam é gigantesco, então, precisamos de alguma forma filtrar os jogos mais indicados para cada usuário e deixar a home mais personalizada.
Dataset
O Dataset que iremos utilizar é uma adaptação da base disponibilizada no Kaggle que contém dados de interação dos usuários com os jogos, indicando quais jogos o usuário já comprou dentro da plataforma.
Cada registro indica que o usuário (user_id) jogou um jogo em específico (content_id). Nossa preferência implícita aqui é a mais simples possível, indica apenas que o usuário demonstrou interesse ao comprar o jogo.
Essa lista de interações deve ser convertida para a matriz de interação, a matriz esparsa que representa as interações de todos os usuários com todos os conteúdos. Uma matriz de tamanho N x M (total de usuários x total de jogos), que no dataset utilizado tem apenas 0,5% dos valores preenchido.
Para o nosso exemplo a matriz de interação será binária. Se o usuário comprou o jogo o valor será 1, se não, o valor será 0.
Caso fosse uma preferência explicita/implícita como a nota dada, a quantidade de horas jogas no jogo ou quantidade de vezes que o usuário visualizou o jogo, esses valores seriam decimais.
Nessa modelagem estamos indicar que o valor 1 significa que o usuário gostou do jogo e 0 algo indefinido, pois, não podemos afirmar que ele não gostou. O zero é nosso coringa e o valor que deve ser previsto pelo modelo após o treinamento.
Perceba que cada linha da matriz (users_item_matrix_df[user_id]) é um vetor que caracteriza o usuário (features) pelo histório de interações:
Game ID.: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
View: 0 0 1 0 1 1 1 0 0 0 1 1 0 1 1 ...
Esse vetor contém todos os jogos que o usuário já comprou na plataforma da Steam. A mágica do AE acontece ao comprimir essa informação e posteriormente reconstruí-la, o vetor deixa de ser esparso e se transforma em uma estimativa completa.
Arquitetura
A arquitetura do modelo utilizado é uma adaptação do artigo original publicado em 2107 pela NVIDIA:
KUCHAIEV, Oleksii; GINSBURG, Boris.
Training deep autoencoders for collaborative filtering.
arXiv preprint arXiv:1708.01715, 2017.
https://arxiv.org/pdf/1708.01715.pdf
O modelo consiste em um AE padrão, onde cada camada do encoder e decoder é uma camada Densa de redes neurais.
A entrada (x) é o vetor do usuário em sua forma esparsa (a linha da matriz); esse vetor é codificado pelos layers do encoder para criação do espaço-latente z; posteriormente z é decodificado pelos layers do decoder para retornar um vetor do tamanho original (y) totalmente preenchido.
4. Implementação do Modelo em Keras
O código abaixo implementa uma versão da arquitetura apresentada por KUCHAIEV, o modelo contém apenas um layer no encoder e um no decoder de tamanho 512, o espaço-latente no centro do modelo tem 256 dimensões.
Modelo
A entrada do modelo é o vetor de interações de tamanho 4862 (total de jogos na base), esse vetor é reduzido a um espaço-latente de 256D e posteriormente recriado como um vetor de tamanho 4862. A quantidade de camadas no encoder/decoder e o tamanho das camadas são hiperparâmetros do modelo e devem ser otimizado para o problema. Não existe uma resposta correta ou relação com o tamanho da entrada, mas sim com a complexidade do problema para codificar e decodificar de forma satisfatória.
input/output
Como estamos treinando um AE, a entrada e a saída do modelo são as mesmas informações, o vetor de interações de cada usuário no caso:
loss function
A função de loss escolhida foi a Mean Squared Error, que é a média dos erros quadrados entre o predito e o real. O MSE força que o vetor de saída seja próximo do vetor de entrada, mas devido a arquitetura de enc/dec esse vetor será uma aproximação e não igual (o que é ótimo).
Obs: O artigo original utiliza uma função de loss diferente, uma adaptação do MSE. Deixarei para os curiosos o motivo dessa adaptação.
Treinamento
A quantidade de épocas, tamanho do batch, entre outros parâmetros vai depender do problema também. O ideal é treinar até que a curva de loss se estabilize e o erro pare de reduzir.
Train on 3381 samples, validate on 376 samples
Epoch 1/50
3381/3381 [==============================] - 6s 2ms/step - loss: 0.0282 - val_loss: 0.0038
Epoch 2/50
3381/3381 [==============================] - 4s 1ms/step - loss: 0.0183 - val_loss: 0.0031
Epoch 3/50
3381/3381 [==============================] - 4s 1ms/step - loss: 0.0137 - val_loss: 0.0027
Epoch 4/50
3381/3381 [==============================] - 3s 1ms/step - loss: 0.0111 - val_loss: 0.0025
Epoch 5/50
3381/3381 [==============================] - 4s 1ms/step - loss: 0.0095 - val_loss: 0.0023
.....................
Recomendação de Conteúdo para o Usuário
Como é realizado a recomendação com o modelo?
Ao utilizar o modelo treinado é possível criar a nova versão da matriz de interação totalmente preenchida com valores relacionados as preferências dos usuários para os conteúdos.
Cada valor na matriz é uma estimativa do valor que utilizamos como preferência implícita ou explícita, dessa forma, este valor está relacionada com a preferência do usuário para o conteúdo. No caso do exemplo, quanto maior o valor, mais recomendando é o conteúdo para o usuário.
Como a matriz está totalmente preenchida, para gerar a recomendação é preciso apenas ordenar os índices pela preferência do usuário. Dessa forma teremos a estimativa da preferência do usuário para todos os conteúdos.
Vamos testar o modelo criando recomendação para dois usuários com históricos diferenciados, os usuários de ID 1011 e 1319.
user_id = 1011
A esquerda são os jogos do historico do usuário, os que já foram comprados. A direita são as recomendações geradas pelo modelo:
O usuário 1011 tem uma preferência por jogos na linha do Half-Life, estilo FPS no caso. As recomendações seguiram essa mesma linha e inclusive recomendou versões mais atuais dos jogos já comprados, como “Half-Life 2 Episode Two” e “Portal 2“, outros como “Lef t 4 Dead” também parecem ser boas sugestões.
user_id = 1319
O usuário 1319 tem uma preferência por jogos na linha do RPG, RTS e TBS. As recomendações seguiram esse padrão ao recomendar jogos como “Skyrim” em Versões/DLCs que ainda não foram compradas e jogos como “Civilization” e “Fallout” .
As recomendações parecem fazer sentido \o/.
Existem formas mais interessantes de validarmos a qualidade dos modelos com métricas de RecSys, mas vou deixar esse assunto para outra postagem.
Características do modelo
Assim como Fatoração de Matrizes em geral, o modelo apresentado não permite a recomendação de novos conteúdos sem refazer o treinamento. Isso fica claro ao perceber que a entrada do modelo é um vetor de tamanho fixo, dado pela quantidade de conteúdos utilizados no treinando e funciona como um index para o ID do conteúdo.
Dessa forma, se a utilização for em um contexto que a quantidade de conteúdos cresce rapidamente, como um site de notícias por exemplo, será necessário re-treinar o modelo sempre que quiser gerar recomendações de novos conteúdos (Isso não chega a ser um problema, na prática já funciona assim em modelos clássicos de fatoração de matrizes como ALS e SVD).
Por outro lado, ao contrário da Fatoração de Matrizes, não existe limitação de recomendação para novos usuários, pois, o que caracteriza o usuário é o vetor de histórico de interação, esse vetor é independente da quantidade de usuários na base.
Vizualização do Espaço-Latente
Um forma de entender o que de fato ocorre com o espaço-latente e como podemos utilizar essa informação em outros métodos de machine learning é visualizando esse espaço.
Para isso, vamos separar apenas a parte do Encoder do modelo treinado. Dessa forma o model.predict(X) deve retornar o espaço-latente das entradas e não mais a matriz completa.
Na prática estamos dividindo o AutoEncoder no meio, ficando apenas com a parte que faz a codificação. Essa codificação ou espaço-latente que o encoder cria pode ser utilizada como features melhores em qualquer outro processo de ML.
Em um cenário que a quantidade de features iniciais é muito grande, o AE pode ser treinado justamente para aprender a compactar essa informação e posteriormente utilizar apenas o Encoder desse modelo.
print(latent_space.shape)
=> (3757, 256)print(latent_space[0])
=> [-0.00088539 0.05945319 -0.11359347 -0.01618869 0.04618068
-0.09307779 0.00915201 0.11499665 0.03937069 0.02171459
-0.05518773 0.05511439 0.11921012 0.2148958 0.07757323
0.07551406 -0.09450839 0.02559994 -0.0580176 -0.12241662
0.00083209 -0.03909268 0.2267525 -0.19018269 -0.0421067
...............................
0.04148702 -0.13418546 -0.115382 0.01147429 -0.07583036
0.00889083 -0.01133222 -0.09932269 0.17696847 0.0602184
0.04532129 -0.07835571 0.10255957 0.26352388 -0.05611183
-0.13736586 -0.26091254 -0.1620788 -0.00110002]
O espaço-latente é um vetor denso de dimensão fixa que representa a entrada. Ao visualizar a distribuição do espaço gerado pelo encoder em 2D é possível chegar em algumas percepções. Aqui foi utilizado o t-sne para a redução de 256D -> 2D.
É possível observar alguns clusters entre os usuários que podem indicar perfis de preferências semelhantes. Essa informação pode ser utilizada em campanhas de marketing de novos jogos ou como features.
Um exemplo são os usuários que jogaram “Elder Scrolls” e os que jogaram “Half-Life”, eles estão bem separados nesse espaço e isso reforça a primeira percepção, indica que são perfis de consumo de jogos bem diferentes.
5. Conclusão
Nesse artigo foi apresentado uma introdução os AutoEncoders e como essa arquitetura pode ser útil em diferentes contextos, também foi apresentando algumas características da filtragem colaborativa e como modelar uma solução de sistemas de recomendação utilizando as Deep AutoEncoders.
Embora tenhamos utilizado poucos layers e treinado o modelo apenas com a matriz de interação binária devido a simplicidade, foi possível observar o resultado positivo das recomendações e do espaço-latente criado com o AutoEncoder. Abordagens mais completas poderiam utilizar preferências implícitas melhores, como a quantidade de horas jogadas, nota ou utilizar informações do conteúdo como o nome ou descrição do jogo em modelos de AE hibridos.
O código apresentado no artigo pode ser replicado no Jupyter Notebook .
O projeto completo contendo outras arquiteturas de Deep AutoEncoders treináveis com esse mesmo dataset está no Repositório GitHub. Sinta-se à vontade em baixar, testar, alterar e dar feedbacks.
Referências
Outras referências interessantes