Personalizando as diretrizes de produtos de big data com o Vowpal Wabbit

Oi Meu nome é Nikita Uchetelev. Eu represento Lamoda Research & Development. Temos mais de 20 pessoas e trabalhamos em várias recomendações no site e em aplicativos, estamos desenvolvendo uma pesquisa, determinamos a classificação de mercadorias em catálogos, oferecemos a possibilidade de testes AB de várias funcionalidades e também apoiamos diversos desenvolvimentos internos, como um sistema para prever a elasticidade da demanda e otimizar a logística de entrega.


imagem


Uma das principais direções de desenvolvimento de toda a empresa para os próximos anos é a personalização de nossos produtos e serviços. Tais iniciativas são testadas e implementadas em qualquer lugar - desde a compilação de seleções pessoais de produtos até a escolha de um representante de vendas específico que entregará nossos produtos a você. Como parte do processo de personalização dos produtos de P&D, eu ajo como líder de equipe e, neste artigo, quero falar sobre a plataforma que eu e minha equipe desenvolvemos e desenvolvemos no último ano, bem como sobre os primeiros produtos de P&D personalizados que estão atualmente em teste AB.


Ideologia das recomendações do produto


O atributo de qualquer loja online familiar a todos é a página do produto. Geralmente, fornece uma descrição detalhada, várias fotos grandes, análises de clientes, o botão "Adicionar ao carrinho" e outros elementos de navegação conhecidos. Na parte inferior dessas páginas, há uma ou mais prateleiras com outros produtos chamados "Produtos relacionados", "Compra com este produto" ou qualquer outra coisa. Cada prateleira tem seu próprio objetivo.


Por exemplo, uma prateleira com produtos similares foi projetada para fornecer ao usuário uma variedade adicional de produtos no contexto atual de escolha. Isso pode ser útil se não houver tamanho de usuário no estoque ou se ele estiver no estágio de seleção e desejar o mesmo produto, mas "com botões de pérola". Ao mesmo tempo, a prateleira afasta o comprador da página do produto, para a qual ele não pode mais retornar e, portanto, não a compra.


Há uma segunda prateleira no site e em aplicativos Lamoda, que chamamos de prateleira de recomendações cruzadas . Ele está localizado imediatamente abaixo da prateleira com mercadorias semelhantes, e tentamos colocar nela mercadorias diferentes que são mais frequentemente encontradas em carrinhos de compras, juntamente com o atual SKU (Stock Keeping Unit ou, mais simplesmente, artigo). Assim, por exemplo, calças e sapatos são recomendados para jaquetas, cachecóis e chapéus para blusas. Há um grupo de produtos baratos que são comprados com mais frequência. Como regra, são meias e roupas íntimas, para que possam ser vistas frequentemente nesta prateleira.


Essa técnica de venda é semelhante à upsale . Estamos tentando vender alguns produtos complementares grandes, desde que o usuário goste do produto atual. Ao mesmo tempo, este é um dos poucos lugares onde os clientes podem se familiarizar com a nossa gama. Por exemplo, para ver uma marca ou subcategoria, cuja presença eles desconheciam antes. Chamamos isso de inspiração e descoberta - quando inspiramos os clientes a fazer novas compras e nos dizem o quão ampla é a nossa gama, mostramos preços e descontos.
imagem


Historicamente, o preenchimento dessas prateleiras é calculado offline (com uma margem, caso alguns produtos fiquem sem estoque antes do próximo cálculo), juntamente com a classificação por alguma métrica de similaridade ou conversão condicional. Assim, todos os usuários veem aproximadamente a mesma coisa durante o dia. Decidimos iniciar experimentos com personalização exatamente nessas prateleiras, porque nas páginas dos produtos temos tráfego suficiente para realizar experimentos de qualidade. Do ponto de vista técnico, esse acabou sendo um dos locais mais convenientes para implementação em nossa infraestrutura (marcado em vermelho no diagrama).
imagem


A idéia é a seguinte : treinamos um modelo que pode atribuir o par "usuário + produto" à probabilidade de conversão ou apenas um clique e, em seguida, exibimos na prateleira da esquerda para a direita em ordem decrescente dessa probabilidade. Como apenas 4 a 6 SKUs são exibidos na primeira tela do carrossel, dependendo da resolução da tela, e no total podemos calculá-los, digamos, até centenas, é alcançada uma “profundidade” de personalização bastante aceitável.


Resolvemos o problema a partir do final


Vamos para a parte técnica. Temos limitações no tempo de resposta da API. Por exemplo, em aplicativos, o serviço futuro precisará chegar a tempo de ser responsável por 100 ms. Durante esse período, você precisa acessar bancos de dados diferentes para obter dados de usuários e produtos, organizar centenas de exemplos sob carga de até 100 QPS no pico. Isso nos leva à necessidade de usar estruturas de aprendizado de máquina abaixo de milissegundos. Um dos mais famosos é o Vowpal Wabbit.


Um campo de aplicação típico para essa estrutura é a adtech, ou seja, prever a CTR de um anúncio ao otimizar um lance para um leilão de RTB. Do ponto de vista matemático, podemos colocar um problema semelhante. Suponha que desejamos prever a probabilidade de um clique treinando um modelo na exibição de produtos. A carga no modelo de até 10k QPS é comparável aos indicadores de publicidade e, em geral, justifica a necessidade de se limitar apenas a algoritmos lineares no estágio de prototipagem e MVP.


Vamos agora pensar em quais dados do usuário podem conter o sinal que precisamos e distinguir bem entre os usuários. Como estamos falando de recomendações de produtos e da página do produto que o usuário visita, provavelmente ele está na fase de seleção. Ele tem em sua mente uma certa imagem de "sapatos perfeitos", com a qual compara todos os bens que chamam sua atenção. Deixe-o primeiro ir ao catálogo de produtos da categoria desejada e começar a pesquisa clicando em tudo que mais ou menos se assemelha à sua apresentação. Assim, o usuário reserva algum “rastreamento digital” dos produtos visualizados. Com base na unidade nesta trilha, faremos personalização.


Representações vetoriais de objetos


Todos os produtos diferem entre si com valores tabulares de alguns atributos: cor, materiais, tecidos, tipo de impressão, comprimento da manga, presença de um capuz, altura do salto, etc. Por conseguinte, é possível codificar qualquer traço digital com frações de ocorrência de cada um dos valores desses atributos. Suponha que tenhamos produtos em três cores e três marcas que você pode ver e colocar na cesta. Depois, anotando o histórico das ações de um usuário, ele pode corresponder a um vetor da seguinte forma:
imagem


Nesse caso, o usuário analisou 10 produtos: 5 dourados, 2 pretos e 3 vermelhos. E ele adicionou 2 vermelhos e 2 pretos na cesta, ele não adicionou ouro. Da mesma forma com as marcas A, B e C, bem como com qualquer valor de atributo. Além disso, esse vetor pode ser concatenado com um vetor codificado de um quente de valores de atributo para um produto específico.


Assim, podemos vetorizar um evento específico. Um usuário que, dentro da estrutura da sessão atual, examinou vários produtos com uma determinada distribuição de cores, está se preparando para ver um novo produto, por exemplo, vermelho. Usando dados históricos sobre impressões e cliques, você pode criar um modelo que preveja a probabilidade de um clique em um produto vermelho, desde que ele tenha uma distribuição de cores existente para produtos visualizados anteriormente.


Se você observar o espaço em que aprendemos a exibir panelinhas históricas, poderá ver que uma parte é ocupada por um subespaço binário e a outra é material, e elas não estão conectadas uma à outra. Nosso modelo linear, neste caso, é uma combinação linear (soma ponderada) das coordenadas de pontos nesse espaço. Se ela aprender com esses exemplos, simplesmente aprenderá probabilidades a priori. Por exemplo, o peso na frente da coordenada correspondente à cor vermelha das mercadorias será um valor diretamente proporcional à CTR das mercadorias vermelhas. Assim, as coordenadas específicas do usuário serão ponderadas pelas características de frequência de cliques de diferentes usuários para qualquer produto do catálogo. Mas isso não é exatamente o que gostaríamos.


Recursos polinomiais vêm em socorro - o resultado da multiplicação de todas as quantidades binárias com todas as reais. A estrutura do Vowpal Wabbit possui uma ferramenta poderosa para gerar recursos de energia a partir de namespaces. Vamos tentar compor uma sequência no formato vw para o nosso exemplo, espalhando recursos de usuário e mercadoria por diferentes namespaces.


|user_color :0.5 :0.2 :0.3 |product_color  

Agora, se durante o treinamento adicionarmos a opção -q pu , esses recursos quadráticos diferentes de zero aparecerão:


 user_color^ * product_color^ = 0.5 user_color^ * product_color^ = 0.2 user_color^ * product_color^ = 0.3 

Portanto, o modelo está procurando um sinal não apenas de como os usuários clicam com uma alta proporção de produtos vermelhos visualizados, mas como eles clicam em produtos vermelhos. O peso desse recurso no modelo treinado deve ser positivo e bastante grande.


Essa abordagem da engenharia de recursos aumenta drasticamente a dimensão do espaço em que o treinamento ocorre. Em uma situação em que temos apenas 4 cores, a dimensão desse espaço é 8 (4 cores para o produto e 4 para o usuário). Com a adição de 16 atributos quadráticos, ele aumenta para 24. Na produção, além das cores, usamos mais 13 atributos dos produtos, incluindo, por exemplo, a marca. Portanto, a dimensão total do espaço em que nossos modelos funcionam pode ser de até 3 milhões de recursos. Ao mesmo tempo, queremos manter a proporção do número de exemplos de treinamento para a dimensão do espaço no nível de 1: 100. Para fazer isso, precisamos gerar um total de aproximadamente 300 milhões de observações.


Arquitetura da plataforma de personalização


Armazenamos o fluxo de cliques de nossos usuários no Hadoop (Spark Streaming do Apache Kafka para uma tabela do Hive). Geralmente, obtemos cerca de 30 gigabytes de dados compactados por dia - são mais de cem tipos diferentes de ações que os usuários podem executar no site e nos aplicativos, incluindo a exibição de mercadorias em vários canais.


Também para as prateleiras de recomendações, há informações sobre quais produtos foram exibidos e onde o clique foi feito. Nossa tarefa para cada clique no passado é calcular o estado do usuário em termos da proporção das frações dos valores dos atributos dos produtos visualizados no momento anterior ao clique especificado. A concatenação do par de vetores “usuário” + “produto clicado” dará um exemplo positivo de treinamento, e pares semelhantes com produtos mostrados ao lado na prateleira, mas sem cliques, serão exemplos negativos. É importante que os produtos sejam mostrados nas proximidades. Então podemos ter certeza de que o usuário os viu, mas não clicou. Como bônus, com essa mecânica, podemos controlar a proporção de classes no problema.


Nossa solução é a agregação diária de dados do usuário usando o Spark e o carregamento incremental desses dados no HBase. Considere a estrutura desse agregado.


Portanto, o principal objeto nesta tarefa é o usuário. As sessões são associadas a ele, que consistem em uma sequência de ações, cada uma das quais possui determinadas características, dependendo do tipo de ação. Por exemplo, se estiver visualizando uma página do produto, o número do artigo e o tempo de visualização pertencerão aos atributos que precisamos. Como reserva para o futuro, escrevemos imediatamente no registro a disponibilidade de mercadorias em estoque e dois preços: o básico e levando em consideração estoques e cupons pessoais. Não precisamos de impressões separadamente, portanto, no momento da agregação, elas são atribuídas a cliques e geram um novo tipo de evento no qual, além do campo com o número do artigo do produto em que o clique foi feito, também há uma série de vários artigos ao seu redor no momento da exibição.


O HBase é um banco de dados de colunas com versão com uma interface nativa para conectar-se ao Spark para processamento de batche e com a capacidade de acessar dados por chave. Outra característica é que o HBase não possui o conceito de circuito. Ele pode armazenar apenas bytes, ou melhor, compensar dentro de HFiles especiais que são adaptados à estrutura de blocos do HDFS.


Alguns podem achar controverso escolher um repositório, mas eu tive uma boa experiência trabalhando com o HBase em projetos semelhantes. Além disso, em Lamoda, esse banco de dados já é usado ativamente, por isso não nos custou nada usar um sistema já implantado para MVP. No momento, não usamos a funcionalidade de versão, mas o acesso por chave parecia útil para a possibilidade de treinamento multithread de modelos no futuro e organização da arquitetura lambda para carregamento de dados e outros casos em tempo real.


Como não há esquema no HBase, precisamos de nosso próprio contêiner de dados. Você poderia usar lambda x: json.dumps (x) .encode () , mas eu queria algo mais rápido. Uma solução completamente padrão é usar contêineres protobuf. Como o desenvolvimento de todo o projeto é realizado em python, é mais comum usar a biblioteca pyrobuf personalizada do AppNexus, em vez da biblioteca oficial do Google. Por benchmarks, o desempenho da funcionalidade básica dos protobuffs é várias vezes maior que o original. O esquema aproximado do nosso protobuff é o seguinte:


 enum Location { ru = 1; by = 2; ua = 3; kz = 4; special = 5; } enum Platform { desktop = 1; mobile = 2; a_phone = 3; a_tablet = 4; iphone = 5; ipad = 6; } message Action { enum ActionType { pageview = 1; quickview = 2; rec_click = 3; catalog_click = 4; fav_add = 5; cart_add = 6; order_submit = 7; } required uint64 ts = 1; required ActionType action_type = 2; optional string sku = 3; required bool is_office = 4; repeated string skus = 5; optional uint32 delta = 6; optional string sku_source = 7; optional bool stock = 8; optional uint32 base_price = 9; optional uint32 price = 10; optional string type = 11; } message Session { required string session_id = 1; repeated Action actions = 2; required uint64 session_start = 3; required uint64 session_end = 4; optional uint32 actions_count = 5; } message LID { required string uid = 1; repeated Session sessions = 2; required Location location = 3; required Platform platform = 4; optional uint32 sessions_count = 5; } 

Em resumo, existe um objeto "Usuário" (LID, Lamoda ID). Dentro dela, há uma matriz de objetos "Session", cada um dos quais é uma matriz de objetos "Action". Dividimos as ações por tipo e as armazenamos em diferentes Famílias de Colunas, o que nos permite otimizar um pouco a leitura quando precisamos apenas de eventos de determinados tipos (visualizações do produto, cliques atribuídos a diferentes tipos de recomendações etc.).


Teste


Durante três semanas, realizamos um teste AB no site lamoda.ru para desktop, com o seguinte design:


  • Controle: as recomendações da API referem-se ao serviço de personalização, aguardam o resultado, mas fornecem os produtos no pedido original, o mesmo para todos.
  • Teste: os produtos são exibidos da esquerda para a direita em ordem decrescente de previsão de probabilidade de clique.

A divisão em duas opções é baseada na LID do usuário - essencialmente por seu cookie. Nossa plataforma experimental garante que as observações coletadas em duas versões sejam independentes e distribuídas uniformemente, e as alterações métricas sejam avaliadas com um nível de significância de 5% (valor de p 0,05). Como resultado, recebemos + 10% de CTR de toda a prateleira e uma mudança positiva significativa na receita. Na semana passada, lançamos essa funcionalidade para todos os usuários do site.


Para verificar a personalização, basta olhar para vários produtos diferentes e comparar a classificação das recomendações em uma das prateleiras "Eles compram este produto" em dois navegadores diferentes ou use o modo "Incógnito". Obviamente, se você ainda não visitou nosso site, ainda há poucos dados sobre você na plataforma. Tente escolher uma jaqueta de inverno e compare a ordem dos produtos novamente depois de um tempo.


Do serviço à plataforma


Portanto, temos uma plataforma inteira - um conjunto de ferramentas de software que agregam dados e os armazenam, além de uma estrutura para vetorizar objetos de negócios em um momento arbitrário no passado, o que permite criar modelos de pontuação para avaliar a probabilidade de várias ações. A interferência do modelo é fornecida por meio de um serviço da web que pode coletar vetores relevantes de várias fontes de dados e executá-los através do modelo. Ele aceita uma LID (identificador de usuário), uma lista de SKUs que precisam ser abertas e várias informações adicionais, retornando a mesma lista de produtos às previsões de probabilidade de clique enriquecidas com previsões. Abaixo está um diagrama da arquitetura conceitual de nossa plataforma:
imagem


O elemento ML Core é um conjunto de máquinas virtuais nas quais os clientes Hadoop e os funcionários do Airflow estão instalados. Definimos a configuração, com quais parâmetros para treinar o modelo, onde obter dados históricos e assim por diante. Como resultado, o modelo é treinado e publicado em artefatos, e as informações sobre o processo de aprendizado e as métricas de qualidade de interesse para nós são armazenadas no meta-repositório.


Já está testando ou se preparando para testes do sistema de personalização de recomendações nas listas de discussão, nas páginas principais e na prateleira, com recomendações para produtos similares, segmentos parecidos para segmentar publicidade interna e externa e muito mais.




Este foi um artigo introdutório. Em outras publicações, posso me concentrar nos aspectos técnicos da arquitetura e falar sobre seu desenvolvimento, ou, inversamente, expandir o componente do produto de maneira mais ampla e contar como usamos e avaliamos o ML nas tarefas do produto. Escreva nos comentários seus desejos sobre isso.

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


All Articles