Cómo aprendimos a predecir la solicitud de un usuario y acelerar la carga de resultados de búsqueda

Las sugerencias de búsqueda (sjest) no son solo un servicio de usuario, sino también un modelo de lenguaje muy poderoso que almacena miles de millones de consultas de búsqueda, admite búsquedas difusas, personalización y mucho más. Aprendimos a usar sujest para predecir la consulta final del usuario y cargar los resultados de búsqueda antes de hacer clic en el botón "Buscar".


La introducción de esta tecnología, el pre-renderizador, requirió muchas soluciones interesantes en el desarrollo móvil, el desarrollo de tiempo de ejecución de búsqueda, registros y métricas. Y, por supuesto, necesitábamos un clasificador genial que determinara si se debe cargar una consulta de búsqueda por adelantado: este clasificador debe encontrar un equilibrio entre la aceleración de descarga, el tráfico adicional y la carga de búsqueda. Hoy hablaré sobre cómo logramos crear un clasificador de este tipo.




1. ¿Funcionará la idea?


En las tareas de investigación, rara vez está claro de antemano que existe una buena solución. Y en nuestro caso, nosotros tampoco sabíamos inicialmente qué datos se necesitaban para construir un clasificador suficientemente bueno. En tal situación, es útil comenzar con algunos modelos muy simples que le permitirán evaluar los beneficios potenciales del desarrollo.


La idea más simple resultó ser la siguiente: cargaremos los resultados de búsqueda en el primer aviso de la sugerencia de búsqueda; cuando el aviso cambia, descartamos la descarga anterior y comenzamos a descargar un nuevo candidato. Resultó que dicho algoritmo funciona bien y casi todas las solicitudes se pueden precargar, sin embargo, la carga en los backends de búsqueda aumenta en consecuencia y el tráfico de usuarios aumenta en consecuencia. Está claro que tal solución no se puede implementar.


La siguiente idea también fue bastante simple: es necesario cargar sugerencias de búsqueda probables no en todos los casos, sino solo cuando tenemos la confianza suficiente de que realmente son necesarias. La solución más simple será un clasificador que funcione directamente en tiempo de ejecución de acuerdo con los datos que ya tiene el más seguro.


El primer clasificador se creó utilizando solo diez factores. Estos factores dependían de la distribución de probabilidad sobre el conjunto de indicaciones (idea: cuanto mayor sea el "peso" de la primera solicitud, más probable será que se ingrese) y la longitud de la entrada (idea: cuantas menos letras tiene que ingresar el usuario, más seguro es precargar). La belleza de este clasificador también fue que, para construirlo, no era necesario liberar nada. Los factores necesarios para el candidato se pueden recopilar haciendo una solicitud http al demonio más sagrado, y los objetivos se construyen de acuerdo con los registros más simples: el candidato se considera "bueno" si la solicitud total del usuario coincide por completo. Fue posible armar un grupo de este tipo, entrenar varias regresiones logísticas y construir un diagrama de dispersión en solo unas pocas horas.


Las métricas para el pre-renderizador no están completamente organizadas como en la clasificación binaria habitual. Solo dos parámetros son importantes, pero esto no es precisión o integridad.


Dejar r- número total de solicitudes, p- el número total de todos los prerenders, ep- el número total de pre-procesadores exitosos, es decir los que finalmente coinciden con la entrada del usuario Luego, dos características interesantes se calculan de la siguiente manera:


gastosgenerales= fracp+repr1


eficiencia= fracepr


Por ejemplo, si se realiza exactamente una presentación previa por solicitud, y la mitad de los presentadores son exitosos, la eficiencia de la presentación previa será del 50%, y esto significa que fue posible acelerar la carga de la mitad de las solicitudes. Al mismo tiempo, para aquellas solicitudes en las que la representación funcionó correctamente, no se creó tráfico adicional; para aquellas solicitudes en las que falló la representación previa, se tuvo que establecer una solicitud adicional; así que el número total de solicitudes es una vez y media mayor que el original, las solicitudes "extra" representan el 50% del número original, por lo tanto gastosgenerales=0.5.


En estas coordenadas, dibujé el primer diagrama de dispersión. Se veía así:



Estos valores contenían una buena cantidad de suposiciones, pero al menos ya estaba claro que, muy probablemente, un buen clasificador funcionaría: acelerar la carga para varias decenas de por ciento de las solicitudes, aumentar la carga en varias decenas de por ciento es un cambio interesante.


Fue interesante ver cómo funciona el clasificador. De hecho, resultó que la duración de la solicitud resultó ser un factor muy fuerte: si el usuario ya casi ha ingresado la primera pista, y es bastante probable al mismo tiempo, puede realizar una captación previa. Entonces, la predicción del clasificador aumenta bruscamente hacia el final de la consulta.


margin prefix candidate -180.424   -96.096    -67.425    -198.641    -138.851    -123.803    -109.841    -96.805    -146.568     -135.725     -125.448     -58.615      31.414     -66.754      1.716         

Una entrega previa será útil incluso si ocurrió en el momento de ingresar la última letra de la solicitud. El hecho es que los usuarios aún pasan algún tiempo haciendo clic en el botón "Buscar" después de ingresar la solicitud. Este tiempo también se puede guardar.


2. Primera implementación


Después de un tiempo, fue posible armar un diseño completamente funcional: la aplicación fue a buscar pistas sobre el demonio del más sajest. Envió, entre otras cosas, información sobre si precargar la primera pista. La aplicación, después de recibir la bandera apropiada, descargó los resultados y, si la entrada del usuario finalmente coincidió con el candidato, presentó los resultados.


En ese momento, el clasificador adquirió nuevos factores, y el modelo ya no era una regresión logística, sino un CatBoost . Inicialmente, implementamos umbrales bastante conservadores para el clasificador, pero incluso nos permitieron cargar instantáneamente casi el 10% de los resultados de búsqueda. Fue un lanzamiento muy exitoso, porque logramos cambiar significativamente los cuantiles menores de la velocidad de carga de búsqueda, y los usuarios notaron esto y comenzaron a regresar estadísticamente significativamente a la búsqueda: una aceleración significativa de la búsqueda afectó la frecuencia con la que los usuarios realizan sesiones de búsqueda.


3. Mejoras adicionales


Aunque la implementación fue exitosa, la solución aún era extremadamente imperfecta. Un estudio cuidadoso de los registros de operaciones mostró que hay varios problemas.


  1. El clasificador es inestable. Por ejemplo, puede resultar que por el prefijo Yandex predice la consulta Yandex, por el prefijo Yandex no predice nada, y por el prefijo Yandex nuevamente predice la consulta Yandex. Luego, nuestra primera implementación directa hace dos solicitudes, aunque bien podría administrarse con una.


  2. La representación no sabe cómo procesar pistas palabra por palabra. Al hacer clic en un mensaje palabra por palabra, aparece un espacio adicional en la solicitud. Por ejemplo, si un usuario ingresó a Yandex, su primer aviso sería Yandex; pero si el usuario utilizó la pista palabra por palabra, la entrada ya será la cadena "Yandex", y el primer mensaje será "tarjetas Yandex". Esto conducirá a consecuencias desastrosas: la solicitud Yandex ya descargada será rechazada, en su lugar se cargará la solicitud de la tarjeta Yandex. Después de eso, el usuario hace clic en el botón "Buscar" y ... esperará la entrega completa de la emisión a solicitud de Yandex.


  3. En algunos casos, los candidatos no tienen posibilidades de tener éxito. Una búsqueda de coincidencias inexactas funciona en sujest, de modo que un candidato puede, por ejemplo, contener solo una palabra del usuario ingresado; o el usuario puede hacer un error tipográfico, y luego el primer mensaje nunca coincidirá exactamente con su entrada.



Por supuesto, dejar una reprensión con tales imperfecciones era una pena, incluso si era útil. Me ofendió especialmente el problema con las pistas palabra por palabra. Considero que la introducción de consejos de palabra en palabra en la búsqueda móvil de Yandex es una de mis mejores implementaciones durante todo el tiempo que trabajé en la empresa, ¡pero aquí la representación no sabe cómo trabajar con ellos! Vergüenza, no de otra manera.


En primer lugar, solucionamos el problema de la inestabilidad del clasificador. La solución se eligió de manera muy simple: incluso si el clasificador devolvió una predicción negativa, no dejamos de cargar al candidato anterior. Esto ayuda a guardar solicitudes adicionales, porque cuando el mismo candidato regrese la próxima vez, no necesitará volver a descargar el problema correspondiente. Al mismo tiempo, esto le permite cargar los resultados más rápido, ya que el candidato se descarga en el momento en que el clasificador trabajó por primera vez para él.


Luego llegó el turno de las pistas proverbiales. Un servidor sagest es un demonio sin estado, por lo que es difícil implementar la lógica asociada con el procesamiento del candidato anterior para el mismo usuario. Buscar solicitudes simultáneamente para una solicitud de usuario y para una solicitud de usuario sin un espacio final significa en realidad duplicar el RPS por un demonio más sajest, por lo que tampoco era una buena opción. Como resultado, hicimos esto: el cliente pasa en un parámetro especial el texto del candidato, que se está cargando en este momento; Si este candidato, hasta espacios, es similar a la entrada del usuario, lo regalamos, incluso si el candidato para la entrada actual ha cambiado.


Después de este lanzamiento, finalmente se hizo posible ingresar consultas usando mensajes palabra por palabra y disfrutar de la captación previa. Es bastante divertido que antes de este lanzamiento, solo aquellos usuarios que terminaron de ingresar su solicitud usando el teclado, sin un sjest, usaran la captación previa.


Finalmente, resolvimos el tercer problema con la ayuda de ML: factores agregados sobre las fuentes de sugerencias, coincidencia con la entrada del usuario; Además, gracias al primer lanzamiento, pudimos recopilar más estadísticas y aprender de los datos mensuales.


4. ¿Cuál es el resultado?


Cada uno de estos lanzamientos generó un aumento del diez por ciento en las descargas instantáneas. La mejor parte es que pudimos mejorar el rendimiento de la reproducción más de dos veces, prácticamente sin tocar la parte sobre el aprendizaje automático, sino solo mejorando la física del proceso. Esta es una lección importante: a menudo la calidad del clasificador no es el cuello de botella en el producto, pero su mejora es la tarea más interesante y, por lo tanto, el desarrollo se distrae.


Desafortunadamente, los SERP cargados instantáneamente no son un éxito completo; la salida cargada aún necesita ser renderizada , lo que no ocurre instantáneamente. Por lo tanto, todavía tenemos que trabajar para convertir mejor las descargas instantáneas de datos en representaciones instantáneas de resultados de búsqueda.


Afortunadamente, las implementaciones implementadas ya nos permiten hablar sobre el pre-renderizador como una característica bastante estable; También probamos las implementaciones descritas en la cláusula 2: todas juntas también conducen al hecho de que los propios usuarios comienzan a realizar sesiones de búsqueda con más frecuencia. Esta es otra lección útil: las mejoras significativas en la velocidad del servicio pueden afectar estadísticamente significativamente su retención.


En el video a continuación, puede ver cómo funciona la reproducción en mi teléfono.


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


All Articles