Sistema de recomendação para o Directum Club. Parte Um, Colaborativa

Todos os dias, os usuários em todo o mundo recebem um grande número de correspondências diferentes - somente através do serviço MailChimp envia diariamente um bilhão de cartas . Destes, 20,81% são descobertos.


Todo mês, os usuários de nossos sites recebem boletins com materiais selecionados pelo editor. Cerca de 21% dos leitores abrem essas cartas.


Para aumentar esse número, você pode personalizá-los. Uma maneira é adicionar um sistema de recomendação que levará materiais interessantes para um leitor em particular.


Neste artigo, falarei sobre como implementar um sistema de recomendação do zero, com base na filtragem colaborativa.


A primeira parte do artigo contém a base teórica para a implementação do sistema de recomendação. A matemática da escola é suficiente para entender o material.


A segunda parte descreve uma implementação Python para os dados do site.


Um pouco da teoria da filtragem colaborativa


A filtragem colaborativa é provavelmente a abordagem mais fácil nos sistemas de recomendação. É baseado na ideia de que usuários semelhantes gostam de objetos semelhantes, como artigos.


O que significa "usuários semelhantes"?


quem é você


Como determinar o quanto Vasily é como Ivan ou um artigo sobre SQL Server para um artigo sobre PostgreSQL?


Vejamos um exemplo. Digamos que temos quatro usuários: Vasily, Ivan, Inna e Anna. O site possui cinco artigos: Artigo 1, Artigo 2, Artigo 3, Artigo 4 e Artigo 5. Na tabela abaixo, o número na interseção do usuário e do artigo é a classificação do usuário em uma escala de cinco pontos. Zero na tabela são artigos que não foram classificados pelo usuário. Por exemplo, Vasily gostou dos artigos 1, 3 e 4.


Quadro 1


Artigo 1Seção 2Seção 3Seção 4Seção 5
Vasily40 0550 0
Ivan0 00 0450 0
Inna4240 00 0
Anna550 00 05

Intuitivamente, podemos supor que, se os usuários gostarem dos mesmos artigos, seus gostos coincidirão. O que você acha, cujos interesses são semelhantes aos de Vasily?


Os interesses de Vasily são mais parecidos com os de Ivan e Inna e menos com os de Anna. Por que - será dito mais adiante.


Para trabalhos posteriores, é necessário formalizar e medir a “semelhança” de Vasily e Ivan ou Inna e Anna.


A maneira mais fácil de fazer isso é considerar as classificações dos usuários como uma descrição do seu perfil. No exemplo, cada linha da tabela é uma descrição de um usuário. A primeira linha - a descrição de Basil - é um vetor de cinco números: [4, 0, 5, 5, 0]; o segundo - Ivan - [0, 0, 4, 5, 0]; o terceiro é Inna - [4, 2, 4, 0, 0]; o quarto é Anna - [5, 5, 0, 0, 5].


Agora você pode introduzir o conceito de descrição de usuário "medida de similaridade".


Uma maneira de medir a "similaridade" dos usuários é calcular a distância do cosseno entre os vetores que os descrevem.



A distância do cosseno é calculada pela fórmula:


1cos theta=1 fracA cdotB||A|| cdot||B||


onde A e B - vetores de descrição do usuário; A cdotB - produto escalar de vetores de descrição; ||A|| , ||B|| - comprimentos dos vetores de descrição.


O significado da distância do cosseno é o seguinte: se dois vetores onde A e B (vetores de descrição do usuário) são "semelhantes", então o ângulo entre eles tenderá a zero e o cosseno desse ângulo tenderá a se unir. No caso ideal, quando os "interesses" dos dois usuários coincidem, a distância do cosseno para eles será zero.


Distância cosseno entre Vasily e Ivan:


1cos theta=1 frac4 cdot0+0 cdot0+5 cdot4+5 cdot5+0 cdot0 sqrt42+02+52+52+02 cdot sqrt02+02+42+52+02=0,1349


Da mesma forma, a distância cosseno entre Vasily e Anna é 0,715. Ou seja, os interesses de Vasily são mais parecidos com os de Ivan do que com os de Anna.


Como prever classificações de usuários?


Esta parte é a mais interessante. Existem muitas opções diferentes. Abaixo, consideramos duas opções simples.


Classificação prevista - classificação média entre usuários "semelhantes"


A opção mais fácil para calcular a classificação prevista é ver quais classificações os usuários "semelhantes" colocam no artigo e obter a classificação média:


ru,i= frac1N sumu emUru,i


Nesta fórmula:


  • ru,i A estimativa prevista para i artigo e usuário u ,
  • ru,i - classificação do usuário u para i artigo
  • U - Muitos usuários "semelhantes",
  • N - o número de usuários "semelhantes".

Classificação prevista - classificação média ponderada entre usuários "semelhantes"


Uma opção um pouco mais complicada é levar em consideração o grau de similaridade: as classificações de usuários mais semelhantes devem influenciar a classificação final mais do que as classificações de menos similares:


ru,i= frac sumu inU(1semelhante(u,u))ru,i sumu emU|1simil(u,u)|


Nesta fórmula:


  • ru,i A estimativa prevista para i artigo e usuário u ,
  • ru,i - classificação do usuário u para i artigo
  • U - Muitos usuários "semelhantes",
  • simil(u,u) - “similaridade” (distância cosseno) dos usuários u e u .

Como medir a qualidade das recomendações?



Ao criar qualquer sistema de recomendação, você deve determinar a métrica pela qual pode avaliar a qualidade do nosso modelo - quão bem o sistema oferece ao usuário novos materiais. Por exemplo, o erro quadrático médio da raiz ( RMSE ) É a raiz quadrada do erro médio para todas as classificações de usuários. Formalmente, essa medida é descrita pela fórmula:


RMSE= sqrt frac1|D| sumu,i inD( hatru,iru,i)2


Nesta fórmula


  • D - o conjunto de todas as classificações de artigos por usuários,
  •  hatru,i - classificação prevista do usuário u artigo i ,
  • ru,i - classificação real do usuário u artigo i .

No caso ideal, quando as classificações previstas coincidirem com as do usuário RMSE igual a zero.


Considere um exemplo. Dois sistemas de referência fizeram previsões para Vasily. O resultado está na tabela abaixo.


Artigo 1Seção 2Seção 3Seção 4Seção 5
Vasily40 0550 0
Sistema de recomendação 113522
Sistema de recomendação 241530 0

É intuitivamente claro que o segundo sistema de recomendação previu classificações melhores que o primeiro. Contagem RMSE :


RMSE(1)= sqrt frac(41)2+(03)2+(55)2+(52)2+(02)25=US$2,48


RMSE(2)= sqrt frac(44)2+(01)2+(55)2+(53)2+(00)25=1


O erro nas avaliações do segundo sistema de recomendação deverá ser significativamente menor.


Implementação


Temos à nossa disposição a maioria dos dados sobre artigos e usuários do site: informações sobre artigos, tags, curtidas de usuários, etc.


Para implementar a filtragem colaborativa, as classificações do usuário são suficientes.


Isenção de responsabilidade

A seguir, o código é escrito "na testa" para demonstrar a lógica do sistema de recomendação. Na vida real, é melhor usar todos os recursos de numpy e pandas .


 import pandas as pd import numpy as np import os ratings_df = pd.read_csv('./input/Ratings.csv') print(' :', ratings_df.shape[0]) print(' :', ratings_df[ratings_df['Rate']].shape[0]) unique_user_ids = ratings_df[ratings_df['Rate']]['UserId'].unique() print(' :', len(unique_user_ids)) ratings_df.head() 

Saída [1]

Dados totais: 15313
Avaliações positivas: 15121
Usuários ativos: 1007


IdDocumentIdTaxaID do usuário
0 011Verdadeiro5000
12878Verdadeiro2441
231512Verdadeiro678
341515Verdadeiro678
45877Verdadeiro5110
...............

1007 usuários ativos deram 15313 "classificações". Destes, 15121 "curtiram".


Os dados contêm quatro colunas: um identificador de linha do banco de dados (coluna Id ), um identificador de objeto (coluna DocumentId ), um sinal de que o usuário gostou do artigo (coluna Rate ) e um identificador de usuário (coluna UserId ).


Por conveniência, adicione a coluna RateInt . 1 nesta coluna significa que o usuário gostou do artigo; -1 - isso não gostou.


 ratings_df['RateInt'] = ratings_df['Rate'].apply(lambda x: 1 if x else -1) ratings_df.head() 

Saída [2]
IdDocumentIdTaxaID do usuárioRateInt
0 011Verdadeiro50001
12878Verdadeiro24411
231512Verdadeiro6781
341515Verdadeiro6781
45877Verdadeiro51101

Para trabalhos adicionais, é necessário dividir o conjunto de dados em treinamento e teste: o treinamento será usado para treinar o modelo e o teste determinará a qualidade das previsões.


 from sklearn.model_selection import train_test_split train, test = train_test_split(ratings_df, test_size=0.2) 

Por conveniência, transformamos cada conjunto em uma tabela, onde nas linhas estão os identificadores de usuários e nas colunas estão os identificadores de artigos por analogia com o exemplo no início do artigo.


 def create_matrix(df): ratings_per_user = [] post_ids = df['DocumentId'].unique() for user_id in tqdm_notebook(all_users_ids, ''): row = {'user_id': user_id} ratings = df[df['UserId'] == user_id]['DocumentId'].values for post_id in post_ids: row[str(post_id)] = 1 if post_id in ratings else 0 ratings_per_user.append(row) return pd.DataFrame(ratings_per_user) train_df = create_matrix(train) test_df = create_matrix(test) 

Usuários que combinam com matrizes e artigos favoritos permitem calcular a distância do cosseno entre os usuários:


 from scipy import spatial def cos_distance(x1, x2): return spatial.distance.cosine(x1, x2) at_least_one_fav_post_users = list(train_valuable_df['user_id'].values) def calculate_distances(df): columns = df.columns[:-1] cp = at_least_one_fav_post_users.copy() data = [] for user_id_1 in tqdm_notebook(at_least_one_fav_post_users, ''): row = {'user_id': user_id_1} for user_id_2 in cp: x1 = df[df['user_id'] == user_id_1][columns].values[0] x2 = df[df['user_id'] == user_id_2][columns].values[0] row[str(user_id_2)] = cos_distance(x1, x2) data.append(row) return pd.DataFrame(data) train_distances = calculate_distances(train_valuable_df) 

Agora tudo está pronto para solicitar aos usuários os artigos que eles, em nossa opinião, vão gostar.


Implementamos as duas estratégias para calcular as recomendações descritas acima: as classificações média e média ponderada entre usuários semelhantes.


Primeira maneira


Colocamos 10 usuários mais próximos da atual e prevemos a classificação como média para usuários semelhantes para o artigo:


 from tqdm import tqdm_notebook import heapq def rmse(predicted, actual): return ((predicted - actual) ** 2).mean() ** 0.5 def get_similar(id, n): df = train_distances[train_distances['user_id'] == id] d = df.to_dict('records')[0] top_similar_ids = heapq.nsmallest(n+1, d, key=d.get) top_similar = df[top_similar_ids] return top_similar.to_dict('records')[0] def get_predictions(id, n): top_similar_users = get_similar(id, n) top_similar_users_ids = list([int(x) for x in top_similar_users.keys()]) ratings_for_top_similar = train_df[train_df['user_id'].isin(top_similar_users_ids)] predicted_ratings = {} for article_id in train_df.columns[:-1]: predicted_ratings[article_id] = ratings_for_top_similar[article_id].mean() return predicted_ratings rand_n_users = train_distances.sample(50)['user_id'].values err = 0 for u in tqdm_notebook(rand_n_users): pred = get_predictions(u, 10) err += rmse(test_df[test_df['user_id'] == u][list(pred.keys())].values, pd.DataFrame(pred, index=[0]).values) print(err / len(rand_n_users)) 

Para a primeira abordagem, obtivemos um erro igual a 0,855.


Recomendações para o usuário casual
ArtigoClassificação prevista
DIRECTUM 5.6. Nova pesquisa de texto completo0.6364
DIRECTUM 5.6 - mais opções para um trabalho confortável0.6364
Desenvolvimento de ferramentas de desenvolvimento no DIRECTUM 5.50.6364
DIRECTUM Introduz o DirectumRX0,5455
O lançamento anual do DIRECTUM é agora 5.1!0,5455
A a K. DIRECTUM 5.0 é atualizado novamente0,5455
DIRECTUM Jazz - uma nova solução móvel da empresa DIRECTUM0,5455
Você já atualizou o DIRECTUM?0,5455
DIRECTUM 5.6. Super colunas e ações de pasta0,5455
Destaque da sintaxe do GitLab ISBL0,5455

Segunda via


O segundo método leva em consideração o grau de similaridade dos usuários. Sua implementação é quase idêntica à primeira:


 def get_predictions(id, n): similar_users = get_similar(u, 10) prediction = {} user_ids = list(similar_users.keys()) user_similarities = [] for user_id in user_ids: user_similarities.append(similar_users[user_id]) predicted_ratings = {} for article_id in train_df.columns[:-1]: prediction_for_article = 0 numerator = 0 denominator = 0 for user_id in user_ids: rating = train_df[train_df['user_id'] == int(user_id)][article_id].values[0] numerator += rating * (1 - similar_users[user_id]) denominator += np.abs(similar_users[user_id]) predicted_ratings[article_id] = numerator / denominator return predicted_ratings err = 0 for u in tqdm_notebook(rand_n_users): pred = get_predictions(u, 10) err += rmse(test_df[test_df['user_id'] == u][list(pred.keys())].values, pd.DataFrame(pred, index=[0]).values) print(err / len(rand_n_users)) 

Nesse caso, eles receberam o erro 0.866. O erro é um pouco maior que no primeiro caso.


Recomendações para o mesmo usuário aleatório
ArtigoClassificação
DIRECTUM 5.6. Nova pesquisa de texto completo0,3095
DIRECTUM 5.6 - mais opções para um trabalho confortável0,3095
Desenvolvimento de ferramentas de desenvolvimento no DIRECTUM 5.50,3095
Muitos serviços DIRECTUM - uma ferramenta de administração0,2833
A a K. DIRECTUM 5.0 é atualizado novamente0,2809
O lançamento anual do DIRECTUM é agora 5.1!0,2784
DIRECTUM Introduz o DirectumRX0,2778
Você já atualizou o DIRECTUM?0,2778
DIRECTUM 5.6. Super colunas e ações de pasta0,2758
DIRECTUM Ario - uma nova solução inteligente0,2732

Os resultados podem ser usados ​​em diferentes cenários. Por exemplo, em boletins de notícias de novos artigos por mês ou adicione no site a seção "você pode estar interessado".


Sumário


Neste artigo, tentei em detalhes, usando o exemplo de uma tarefa real, descobrir como criar um sistema de recomendação baseado na filtragem colaborativa.


A vantagem dessa abordagem é sua versatilidade - as recomendações não levam em consideração quais objetos são recomendados. Um sistema pode ser usado para artigos e produtos de blog na loja online.


As desvantagens incluem o seguinte:


  • no caso de um grande número de objetos para recomendações, a matriz objeto-usuário se torna escassa e fica mais difícil encontrar usuários suficientemente semelhantes (menos pares de objetos usuário-objeto correspondem)
  • problema de partida a frio - para um novo usuário, é impossível encontrar usuários semelhantes (existem estratégias para contornar essa limitação, mas elas não são uma panacéia)
  • um sistema baseado em filtragem colaborativa tende a recomendar objetos populares, porque a grande maioria dos usuários apreciará esses objetos.

No próximo artigo, outra abordagem será considerada - com base na análise dos próprios objetos.

Source: https://habr.com/ru/post/pt463609/


All Articles