Cómo creo un servicio de recomendación comunitaria VKontakte

El verano estaba terminando, agosto era especialmente frío. Comencé mi undécimo grado y me di cuenta de que ahora es la última oportunidad (spoiler: no) para mejorar de alguna manera mi competencia profesional. Durante varios años he estado haciendo diligentemente varios proyectos de TI, algunos solos, otros en el equipo. Pero ahora todos los hijos de la novia de mi madre ya están haciendo algo hermoso. Quizás inútil, pero hermoso en apariencia. Alguien hace una simulación adhesiva de partículas en forma de gifs, alguien se sumerge en el aprendizaje automático y realiza todo tipo de transferencias de estilo. ¿Y qué soy peor? Yo también quiero!

Un ejemplo de simulación también está bajo cat.


Ejemplo de simulación de partículas de un amigo mío

Fue con este pensamiento que comenzó mi estudio del tema del aprendizaje automático. Y en términos de capacitación para mí, no había nada nuevo, ya que en cualquier otro campo de TI, se necesita práctica aquí. ¿Pero qué pasa si no estoy interesado en ningún analizador de tonalidad? Debemos inventar algo propio.

Una vez más hojeando las noticias de VKontakte, me di cuenta de que las comunidades de esta red social son un verdadero tesoro para la ciencia de datos. Si procesa texto, memes en forma de imágenes o música de colecciones, puede obtener una gran porción de información relevante sobre personas modernas: géneros musicales populares, vocabulario o la hora del día de la actividad de la mayoría de las personas. Este es un campo para muchos descubrimientos.

Pero, ¿cuánto se necesitarán tales estadísticas para la gente común? ¿Como si echara de menos mi música o no puedo acceder a la sección "Popular"? Por lo tanto, debe pensar en algo práctico, algo que pueda atraer al menos a un porcentaje significativo de usuarios.

Vale la pena señalar que un par de meses antes leí un artículo genial sobre la creación de mi motor de búsqueda de casas , que realmente me impactó. Al igual que el autor, sentí un gran anhelo por grandes proyectos que procesan una gran cantidad de miles de gigabytes de información día y noche.



Y ahora, volvemos a agosto, que se volvió un poco más cálido que al comienzo del artículo. Cuando me di cuenta de que ahora tengo una gran fuente de información, me di cuenta de que era el momento. Ha llegado el momento de nuestro propio sistema monstruoso. Pero la pregunta principal permaneció unos días después: ¿qué debo hacer con todo esto? ¿Qué ofrecer al usuario? No atormentaré al lector, solo le diré que, como algunos de mis amigos, es muy difícil para mí buscar nuevos grupos de VKontakte que me gusten. Ahora cada primer público tiene un nombre: un conjunto aleatorio de palabras. Los administradores intentan que sea lo más absurdo, probablemente esta es una especie de carrera, comprensible solo para ellos.

Y luego decidí escribir un servicio que ayude al usuario a elegir las comunidades, recomendar a qué puede suscribirse. Entonces surgió mi idea.

La introducción no fue larga por casualidad, se suponía que transmitía mis emociones y mostraba que la idea no apareció de la nada. En realidad, como cualquier otra idea sobre Habré.

Mi servicio todavía está en funcionamiento, ha estado funcionando durante más de cuatro meses (si cuenta desde el momento del primer experimento exitoso). Pero ya tengo experiencia que quiero compartir con ustedes. Ahora habrá una descripción breve y más concisa del proyecto. A continuación, describiré algunos puntos clave. Y si el artículo atraerá especialmente a los habrayuzers, saldrá su continuación, en la que habrá información y código más puramente técnicos.

Todo consta de tres partes:

  • buscar bot (si puedes llamarlo así)
  • motor de procesamiento de datos
  • sitio para usuarios (con un panel de control y monitoreo para administradores)

La funcionalidad del bot incluye la búsqueda de nuevos grupos y la "eliminación" de texto y otra información en la base de datos. El motor está involucrado en el procesamiento adicional de estos datos, sobre lo que escribiré a continuación. Y el sitio simplemente permite a los usuarios usar todo esto.

Buscar bot


Nada nuevo aquí. Solo tomo el perfil de una persona en VK y obtengo una lista de grupos y sus amigos de él. Todo esto sucede con la API de VK. Y si este IPA logra obtener una lista de grupos del usuario y sus amigos, entonces no logra obtener el contenido de los grupos ... Simplemente encuentro una restricción y eso es todo. Entonces recordé que hace algún tiempo, VKontakte estaba promocionando su sistema genial solo para esas cosas. Y el nombre de este sistema es Streaming API.
Streaming API: una herramienta para obtener una selección aleatoria de registros de VK. Está escrito en la página de descripción que es solo que puede obtener hasta el 1% de toda la información, para obtener hasta cien debe escribir al Soporte y explicarles sus intenciones.
Parece que todo es maravilloso. Pero no Yo, como probablemente muchos, me he perdido la palabra más importante en la descripción anterior. Y esta es la preposición "antes". Nadie te dará todos los 100% de los datos. Esta es solo una hermosa barra superior y eso es todo. De hecho, nos ponemos así:


Espero que el Agente # 365 no me odie los 365 días del año por esta captura de pantalla

Es decir, solo puedo obtener 30K eventos por día. Y este número incluye comentarios y solo vuelve a publicar. También es necesario indicar algunas palabras de etiqueta, solo vendrán mensajes con ellas. Algunas de las publicaciones restantes simplemente no me interesan, ya que está en el muro de los usuarios. Queda bastante. Como referencia, en mi implementación actual puedo recibir hasta 8,5 millones de registros en unos días de tiempo de actividad incompleto (en total, aproximadamente 10 horas, pero no hubo mediciones precisas).

Aquí tengo que decir acerca de una regla que identifiqué de todo este experimento. Nunca juzgues a un grupo por una publicación. Especialmente si eres una inteligencia artificial susceptible a tal ruido. Por lo tanto, necesita al menos algunas publicaciones para crear una descripción objetiva del público. Ahora, calculemos que algunos grupos con contenido de muy alta calidad lo lanzan una vez cada pocas semanas. E incluso entonces puedo omitirlo debido a la API de transmisión imperfecta. Y si lo consigo, ¿cuánto tiempo debo recopilar el contenido poco a poco?

Lo decidí demasiado tiempo y me fui para otro lado. Como no puedo obtener una respuesta clara de VKontakte en formato JSON, analizaré los muros de las comunidades. Sí, la tarea es un poco complicada y su solución se ralentiza, pero no tengo alternativa. Así es como comencé a escribir el primer bloque de mi sistema. Lo escribí, por cierto, en Java usando Jsoup, una biblioteca que hace que sea muy conveniente extraer contenido del texto HTML. No me olvidé de procesar la fecha de publicación de la última publicación, no necesito comunidades muertas, simplemente no las indexo. Las publicaciones etiquetadas con anuncios también se descartan. No todos los administradores toman tales notas, pero este problema no es tan fácil de resolver, no pude crear un filtro publicitario adecuado y por eso, por ahora, rechazo este filtrado.

Motor


Esta es probablemente la parte más interesante del proyecto, pero no describiré todo en detalle en esta publicación. Si alguien estará interesado en los detalles, pregúnteme de todas las maneras posibles.

La forma más sencilla de presentar texto en un formato comprensible para una red neuronal es la bolsa de palabras.



Proceso de vectorización y más sobre BOW
Preparo un diccionario de todas las palabras comunes de antemano (sin olvidar excluir las que son muy frecuentes, como "a", "qué", "qué" y otras; no distinguen su texto del fondo de otras), cada palabra tiene su propio número. Luego, cuando necesito procesar el texto con una red neuronal, obtengo el número de cada palabra del diccionario (si está allí) y obtengo un vector (también conocido como matriz en la programación). Este es un conjunto ordenado de números, en el que hay una unidad en el lugar de cada número de palabra del texto (vea la imagen de arriba). Resulta un tipo de datos que es bastante comprensible para la red. Tengo la longitud de cada vector es de 30,000, aproximadamente tantas palabras adecuadas que recopilé en las primeras etapas de desarrollo.

También es importante no olvidar que, por ejemplo, las palabras "habr" y "(c) habr" son casi las mismas para entender. Pero para el algoritmo descrito anteriormente, estas son palabras completamente diferentes. Para solucionar esto, uso el analizador morfológico JMorphy2 . Este es el puerto del PyMorphy2 original para Java. Puede hacer muchas cosas interesantes, por ejemplo, cambiar la forma de una palabra (caso, género, número, etc.). Lo necesito para obtener la forma inicial de la palabra. Como saben, las formas iniciales de palabras "idénticas" son las mismas. Y esto resuelve el problema anterior.

 6929  21903  25126  11441  7374  1925  1626  23128  6241  25584 

Un ejemplo de una lista de palabras en un diccionario y sus números (separados por un espacio)

La lista anterior muestra que la palabra "dragón" no se convirtió en "dragón". Esto es un poco incorrecto, pero incluso dicho procesamiento previo de texto es suficiente. En general, esta biblioteca tiene muchos errores, pero la mayoría de ellos no afectan el funcionamiento del sistema.

El servicio está dirigido a un público de habla rusa. Y por simplicidad, solo el ruso se está procesando actualmente. Todos los caracteres (letras entre ellos) de otros alfabetos son desechados, como signos de puntuación, números, emojis ... Nuevamente, simplificación. Además, no olvide filtrar palabras de idiomas que usan parcialmente el alfabeto ruso, pero agregue sus propias letras (ucraniano, por ejemplo).

Pero continuaré desde el momento en que cualquier publicación de VKontakte ya se haya convertido en un vector (lo llamo vectorización). Aquí se conecta el siguiente enlace: una red neuronal. Decidí usarlo porque era interesante para mí y logré encontrar una arquitectura adecuada para mi tarea. El primer artículo de la serie "Codificadores automáticos en Keras" me ayudó con esto. Y sí, decidí usar el codificador automático más común, ya que es beneficioso en términos de velocidad y entrenamiento. Pero hablemos de todo en orden.

Como para todos los demás codificadores automáticos, debe crear dos redes neuronales (codificador y decodificador) y combinarlas en una. Lo hice de la siguiente manera:

 from keras.layers import Input, Dense, Flatten, Reshape from keras.models import Model #    encoding_dim = 64 #  inp = Input(shape=(30000, )) # 30000 -  #     encoded = Dense(encoding_dim, activation='linear')(inp) #  #     input_encoded = Input(shape=(encoding_dim,)) decoded = Dense(30000, activation='sigmoid')(input_encoded) #          encoder = Model(inp, encoded, name="encoder") decoder = Model(input_encoded, decoded, name="decoder") autoencoder = Model(inp, decoder(encoder(inp)), name="autoencoder") 

Pero, ¿por qué necesitamos dos redes? De todos modos, el autor, ¡no describiste por qué esto es todo !

Calma, ahora todo estará. Para entrenar, por ejemplo, solo un codificador es imposible: simplemente no estará claro qué tan correcta fue la predicción que hizo. Y para esto, entrenamos la segunda red, que decodificará inmediatamente la salida de la primera (decodificador). También se utilizan los mismos datos de entrada y salida. Un grupo de dos redes (se llama autoencoder) aprende a obtener lo mismo de los datos de entrada. Pero todos los datos pasan por un estrecho "cuello de botella" en forma de 64 neuronas. Esto descarta la información más innecesaria. Por lo tanto, las redes neuronales aprenden a transmitir información importante sobre el texto con la máxima calidad y arrojan todo el ruido. Luego solo elimino el decodificador y listo. Puede obtener un mejor resultado, pero luego debe aumentar la dimensión de la capa de salida del codificador / decodificador de entrada. Entonces será necesario almacenar más valores en la base de datos, pesará más + todas las operaciones en vectores largos serán más largas (más sobre eso más adelante). O puede agregar capas / neuronas, pero luego el entrenamiento y la vectorización serán más largos.

El codificador en sí mismo le permite "comprimir la dimensión del vector". ¿Recuerdas ese vector de ceros y unos? Entonces, el codificador le permite cambiar su tamaño de 30K a 64 sin mucha pérdida de información importante. Después de este paso, normalmente puede comparar los dos vectores para determinar su similitud ...

Pero miramos el trabajo del servicio por recomendación de las comunidades VK, y no los registros individuales. Esto significa que necesitamos obtener de alguna manera el vector de todo el público. Esto se hace muy fácilmente, matemáticas en el quinto grado. Este es un método un poco burdo, pero funciona. Simplemente tomo y agrego todos los vectores de registro de una comunidad (por ejemplo, tomo tres vectores pequeños {1, 2, 3}, {2, 3, 4}, {0, 4, 2}, obtenemos el vector {3, 9, 9 }). Y divido cada elemento por el número de vectores (obtenemos el vector {1, 3, 3}). Eso es todo, combinamos todos los registros del grupo en uno. En el futuro, debe idear algo más complicado, para poder emitir ruidos en forma de publicaciones con publicidad, por ejemplo. Pero ahora esto es suficiente.

Pasamos a la parte matemática en sí, pero como todos le tienen miedo por alguna razón, la firmaré tanto como sea posible. Comencemos con los vectores en sentido matemático. El vector es un segmento dirigido. Esto es lo que tiene las coordenadas del principio (es más conveniente tomarlas con ceros) y las coordenadas del final. Es este último el que se registra entre llaves. Por ejemplo, las coordenadas del final del vector {1, 0, 1} youtube es un punto con coordenadas (1, 0, 1). Pero consideraremos dos vectores bidimensionales,  veca{5, 2} y  vecb{5, 0}. Vamos a construirlos en un sistema de coordenadas:



Deja el vector  vecaarosa  vecba- amarillo Entonces, por el hecho matemático de la novena clase, el coseno del ángulo entre ellos es igual a la razón de su producto escalar al producto de sus módulos.

Producto escalar <  veca,  vecb> igual a la suma de los productos de los elementos correspondientes, tenemos < veca, vecb>=55+20=25.

El módulo vectorial se encuentra mediante la siguiente fórmula:

| veca|= sqrtax2+ay2


Donde axy ayEste es el primer y segundo valor del vector a, respectivamente. Entonces
|a|= sqrt52+22= sqrt29
|b|= sqrt52+02=5

Combinando todo de acuerdo con la fórmula, obtenemos:
cos( veca, vecb)= frac25 sqrt295 aprox.0.93
La exactitud de los cálculos se puede verificar a través de las funciones trigonométricas del triángulo rectángulo formado. En el proyecto, todos los cálculos se llevan a cabo de acuerdo con dichas fórmulas, pero solo las coordenadas del final del vector no son dos, sino sesenta y cuatro.

¿Qué da esta información? Al final resultó que, cuanto mayor es el valor del coseno (menor es el ángulo), más similares son los textos que corresponden a los vectores. Por lo tanto, la tarea de encontrar el grupo más similar al grupo A se reduce a encontrar el coseno del ángulo entre el vector de este grupo y todos los demás. Luego, el motor deja todos los grupos para los cuales el valor del coseno junto con A será mayor que, digamos, 0.99. En esta etapa, simplemente puede mostrar el resultado, como lo hice antes. Pero este proceso ya es muy largo en 100K comunidades, y ¿qué pasará, digamos, en 1M?

Para resolver este problema, uso el gráfico. Todos los grupos están representados como sus vértices, y dos puntos están conectados si el coseno del ángulo entre los vectores que les corresponde es mayor que 0.99. Pero si no comprende la estructura con el nombre del gráfico, puede imaginar que precalculo los pares de comunidades más similares en la base de datos y los guardo. Y no me olvido de actualizar el gráfico a medida que se agregan nuevos grupos a la base de datos. Sí, es mucho tiempo, pero aún más fácil para el usuario que antes.

Sitio


No pintaré todo sobre el sitio, ya que esta es la parte más fácil y aburrida. Nunca he escrito sitios desde cero, siempre he usado varios motores listos para usar. Pero en este proyecto, me di cuenta de que sería más fácil hacer un samopis. Entonces, el motor del sitio está escrito en Python 3 usando Flask. Y se utiliza el motor de plantillas Ninja2, lo que hace que sea más conveniente sustituir valores dinámicos en código HTML estático (y js). No olvidé la autorización a través de VKontakte, ya que esta es la opción más óptima. El diseñador, como el diseñador de diseño, es simplemente horrible conmigo, si alguien quiere unirse al proyecto, bienvenido.


La primera línea de resultados del sitio.

Los problemas


Encontré algunas situaciones desagradables que resolví con éxito. El problema con la API de VK se escribió anteriormente y su solución fue especialmente desagradable para el servicio, ya que la velocidad bajó mucho. Si antes recibía cien publicaciones en una solicitud, ahora necesito hacer algunas descargas de código HTML grande, analizarlo y solo después de procesarlo. Ahora hay un problema con la restricción de obtener usuarios, sus amigos y grupos, pero este límite realmente no interfiere en esta etapa. Luego tienes que resolverlo de la misma manera que el primero.

El texto en Internet moderno es cada vez menos significativo cada día. Durante muchos años, VKontakte tiene muchos grupos con videos, fotos y música. Y para obtener buenas recomendaciones, debe procesarlas.



Pero esto no es texto, y se necesita una potencia informática realmente seria. Por ejemplo, esta es una tarjeta de video de gama alta, pero ahora no tengo una y no quiero tomar un servidor para todo esto (es demasiado pronto). Pero en general, ya tengo las mejores prácticas para la arquitectura de redes neuronales para esta tarea. Voy a usar algunas neuronas para clasificar las imágenes, "cortando" la parte superior, que es responsable de la clasificación de los objetos. Todo lo que queda será aquello que mapeará los signos de la imagen. Puedo comprimir esta tarjeta con otro codificador y eso es todo, todas las operaciones posteriores son similares a las de "texto".

Queda otra pregunta sin resolver sobre cuántas solicitudes al sitio VKontakte puedo hacer por unidad de tiempo. O en un dia. Ahora no me he encontrado con esta restricción, pero puede suceder en el momento más inoportuno.

Planes futuros


Necesito urgentemente un hermoso panel de control y estadísticas. Ya está en su estado inicial, pero necesita ser terminado. Desde allí quiero controlar el inicio / parada de los microservicios (es decir, el motor consiste en ellos), el tamaño de las colas, la velocidad de procesamiento y todo eso. Bueno, estadísticas, ¿quién no querría ver sus números? Por supuesto, necesito optimizar todo y hacerlo adecuado para los usuarios, en particular, necesito rehacer la parte externa del sitio, ya que no cumple con mis estándares de conveniencia.

Conclusión


Logré emprender el camino de crear un servicio con una estructura interesante (al menos para mí) que voy a utilizar para uno de los concursos que me permitirán ingresar a la mejor universidad rusa (no diré qué tipo de primer no clásico es). Creo que si todavía trabajas, puedes sacar algo más interesante, por ejemplo, un analizador de la calidad de las publicaciones, hacer un servicio de análisis para la administración de la comunidad u otra cosa.

Me encontré con muchas cosas del texto anterior por primera vez. Esto significa que podría hacer algo mal. Si mis lectores saben qué se puede mejorar / solucionar, dónde puedo tener otros problemas, etc., escriba sobre esto en el comentario. Y le pido que critique la calidad del artículo, para que pueda mejorarlo la próxima vez. Gracias

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


All Articles