Preços dinâmicos ou How Yandex.Taxi prevê alta demanda



Antes, para ligar para um táxi, eles tinham que ligar para diferentes números de serviços de expedição e aguardar a entrega do carro por meia hora ou mais. Agora, os serviços de táxi são bem automatizados e o tempo médio para entrega dos carros Yandex.Taxi em Moscou é de cerca de 3-4 minutos. Mas vale a pena chover ou terminar um evento de massa e, novamente, podemos enfrentar uma escassez de carros gratuitos.

Meu nome é Anton Skogorev, chefio o grupo de desenvolvimento de desempenho da plataforma Yandex.Taxi. Hoje vou contar aos leitores da Habr como aprendemos a prever alta demanda e a atrair motoristas para que os usuários possam encontrar um carro grátis a qualquer momento. Você aprenderá como é formado um coeficiente que afeta o valor do pedido. Tudo o que está longe de ser tão simples quanto possa parecer à primeira vista.


Desafio dinâmico de preços


A tarefa mais importante da precificação dinâmica é sempre oferecer a oportunidade de pedir um táxi. É alcançado usando o coeficiente de preço de pico, pelo qual o preço calculado é multiplicado. Chamamos isso simplesmente de surto. É importante dizer que o aumento não apenas regula a demanda por táxis, mas também ajuda a atrair novos motoristas para aumentar a oferta.

Se o aumento for muito grande, reduziremos muito a demanda, haverá um excesso de carros gratuitos. Se definido muito baixo, os usuários verão "sem carros gratuitos". Você precisa escolher um coeficiente no qual caminharemos sobre o gelo fino entre a falta de carros gratuitos e a baixa demanda.

Do que esse coeficiente deve depender? Imediatamente vem à mente a dependência do número de carros e pedidos em torno do usuário. Agora você pode simplesmente dividir o número de pedidos pelo número de drivers, obter o coeficiente e transformá-lo em nosso pico com alguma fórmula (possivelmente linear).

Mas há um pequeno problema nesta tarefa - pode ser tarde demais para contar os pedidos ao redor do usuário. Afinal, um pedido é quase sempre uma máquina já ocupada, o que significa que um aumento em nosso coeficiente sempre será atrasado. Portanto, consideramos não pedidos criados, mas intenções de solicitar pinos de carro. Um alfinete é uma etiqueta "A" em um cartão que um usuário coloca ao iniciar nosso aplicativo.



Vamos formular o problema: precisamos ler os valores instantâneos de máquinas e pinos em algum momento do usuário.

Contamos o número de pinos e carros ao redor


Quando a posição do pino muda (o usuário seleciona o ponto “A”), o aplicativo do usuário envia novas coordenadas e uma pequena folha de informações adicionais ao back-end, o que ajuda a avaliar o pino com mais precisão (por exemplo, a tarifa selecionada).

Tentamos aderir à arquitetura do microsserviço, em que cada microsserviço está envolvido em tarefas separadas. O microsserviço do cirurgião está envolvido no cálculo do aumento. Ele registra os pinos, os salva no banco de dados e também atualiza a pepita dos pinos na RAM, na qual eles se encaixam muito bem. O atraso do cache durante esse trabalho é de apenas alguns segundos, o que é aceitável em nosso caso.

Algumas palavras sobre o banco de dados
Após o registro, cada pino é adicionado de forma assíncrona ao MongoDb com o TTL Index , onde TTL é a "vida útil" do pino, na qual consideramos ativo para calcular o coeficiente de elevação. O usuário não espera enquanto executamos essas ações. Mesmo que algo dê errado, perder um alfinete não é uma tragédia tão grande.



Um cache quente é criado com um índice de geo-hash . Agrupamos todos os pinos por geohash e, em seguida, coletamos os pinos para o raio desejado em torno do ponto do pedido.

Fazemos o mesmo com os carros, mas em outro serviço chamado Tracker, para o qual o Surger simplesmente faz a pergunta "quantos motoristas existem nesse raio".

Portanto, consideramos os valores instantâneos do coeficiente.



Armazenamento em cache


Caso : você está no Garden Ring em Moscou e deseja reservar um carro. Ao mesmo tempo, o preço sobe com frequência e isso é irritante.

Já conhecendo a mecânica, pode-se entender que isso pode ser devido ao fato de que os motoristas se acumulam em um semáforo condicional no momento da solicitação de pico e também saem rapidamente. Por esse motivo, o aumento e o preço podem "saltar" visivelmente.

Para evitar isso, armazenamos em cache o valor do aumento por usuário. Quando um usuário procura um surto, procuramos ver se existe um valor de surto salvo para esse usuário em um raio aceitável (um tour linear de todos os surtos salvos do usuário). Se houver, devolvemos, caso contrário, contamos com um novo e também o salvamos.

Isso funcionou bem, mas existem outras situações.

Caso : 2 usuários solicitam aumento. Um ordena 30 segundos depois que o outro quando os carros de um semáforo de um caso anterior já foram embora. Temos uma imagem em que 2 usuários que fazem pedidos quase simultaneamente podem ter oscilações visivelmente diferentes.

E aqui vamos de cache por usuário para cache por posição. Agora, em vez de armazenar em cache o valor do aumento apenas pelo usuário, começamos a armazená-lo com o hash geográfico que já conhecemos. Então, quase resolvemos o problema. Por que quase? Porque pode haver diferenças nas bordas dos geoheshes. Mas o problema não é tão significativo, porque temos suavização.

Suavização


Talvez, lendo um caso sobre um semáforo, tenha ocorrido a você que de alguma forma era injusto considerar um aumento instantâneo, dependendo do semáforo. Também achamos que sim, então descobrimos como resolver a situação.

Decidimos emprestar o método de vizinhos mais próximos do aprendizado de máquina para o problema de regressão, a fim de determinar quanto o valor do aumento instantâneo é diferente do que está acontecendo ao redor.

O estágio de treinamento, como na descrição formal do método, consiste em armazenar todos os objetos - no nosso caso, os valores calculados da oscilação no pino, já fazemos tudo isso no momento de carregar todos os pinos no cache. O pequeno é calcular o valor instantâneo, compará-lo com o valor da zona e concordar que não podemos nos desviar muito do valor da zona.

Portanto, obtemos um sistema com uma resposta rápida aos eventos e permitindo que você leia rapidamente o valor do coeficiente crescente.

Surge Driving Card


Para se comunicar com o motorista, precisamos exibir o mapa de picos no aplicativo do motorista - um taxímetro. Isso fornece ao motorista um feedback sobre se há demanda na área em que ele está agora e para onde ele deve se mover para obter os pedidos mais caros. Para nós, isso significa que mais motoristas chegarão à zona com alta demanda e a resolverão.



Vivemos com o paradigma de que o dispositivo do motorista é um dispositivo bastante fraco. Portanto, a renderização da grade hexagonal de sobretensão fica no lado de back-end. O cliente chega ao back-end para blocos. Estas são imagens rasterizadas cortadas para exibição direta no mapa.

Temos um serviço separado que periodicamente coleta moldes de pinos do microsserviço Surger e calcula todas as metainformações necessárias para renderizar a grade hexagonal: onde é qual hexágono e qual pico está em cada um.

Conclusão


O preço dinâmico é uma busca constante de um equilíbrio entre oferta e demanda, para que os usuários sempre tenham carros gratuitos disponíveis, inclusive através do mecanismo de atração de motoristas adicionais para áreas de alta demanda. Por exemplo, atualmente estamos trabalhando em um uso mais profundo do aprendizado de máquina para calcular o aumento. Como parte de uma das tarefas nesta área, estamos aprendendo a determinar a probabilidade de um pino ser convertido em um pedido e levar essas informações em consideração. Há trabalho suficiente aqui, por isso estamos sempre satisfeitos com os novos especialistas da equipe.

Se você estiver interessado em aprender sobre uma parte desse grande tópico com mais detalhes, escreva nos comentários. Comentários e idéias também são bem-vindos!

PS Na próxima publicação, meu colega falará sobre o uso do aprendizado de máquina para prever a hora prevista de chegada de um táxi.

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


All Articles