Introducción . Una startup de visión por computadora que utiliza el desarrollo de bajo costo como concepto central. El equipo es bastante coherente con el espíritu: 3-5
Brevemente sobre el producto (pc + software): un sistema de videovigilancia inteligente conectado a una red local y que produce procesamiento de video por sí solo . Dos requisitos previos: la presencia de una interfaz de usuario con diferentes derechos y la máxima autonomía de los algoritmos.
En el aspecto técnico, no hubo restricciones de hardware, lo principal fue que funcionó bien; pero con lo financiero fueron. Por todo sobre todo ~ $ 500. Por supuesto, solo componentes nuevos y modernos. Su elección no es muy buena, ¡pero la hay!
Decidimos el hardware, luego el software. La elección recayó en una arquitectura de microservicio que usa Docker por alguna razón suficiente.
El desarrollo de funciones pasó de simple y necesario (trabajar con streams y archivos de video) a complejo, con revisión periódica. Reunimos MVP, varios sprints de optimización nos acercaron notablemente a nuestro objetivo preciado: completar los 4 puntos simultáneamente, y no por separado:
- Más de 16 cámaras IP (FHD / 25 fps) Reproducción y grabación en vivo, de eventos o de tiempo
- Funcionamiento en paralelo de todos los algoritmos CV disponibles
- El usuario utiliza la interfaz de forma intensiva sin retrasos: mira las transmisiones
- La carga de la CPU es inferior al 90% y todo funciona (!)
Un poco sobre la pila, la elección recayó en: C / C +, Python + TensorFlow, PHP, NodeJS, TypeScript, VueJS, PostgreSQL + Socket.io y otras pequeñas cosas.
Las funciones implementadas se ocultaron deliberadamente para profundizar más en, quizás, la función más interesante y deliciosa del área de CV y, hasta cierto punto, ML.
"Usuario único"
Un ejemplo de uso es recopilar el historial de visitas de cada visitante específico, y los empleados deben tenerse en cuenta por separado, incluso si no sabemos que se trata de un empleado (Ejemplo: un centro comercial).
Y parece que este problema se ha resuelto más de 100,500 veces y los teléfonos y cualquier otra cosa ya pueden reconocer rostros y recordarlos, enviarlos a algún lugar, guardar. Pero el 95% de las soluciones se utilizan en ACS, donde el propio usuario, tratando de ser reconocido, se para frente a una cámara de 5MP a una distancia de 30-50 cm durante varios segundos hasta que su rostro se comprueba con uno o más rostros de la base de datos.
En nuestro caso, tales condiciones fueron un lujo. Los usuarios se movían de forma errática, mirando su smartphone a una distancia suficiente de la cámara montada en el techo. Además, las propias cámaras presentaban dificultades, la mayoría de las veces se trata de cámaras económicas con 1.3-2MP y algún tipo de reproducción de color incomprensible, siempre diferente.
En parte, este problema se resolvió mediante la formación de especificaciones técnicas para las condiciones de instalación de las cámaras, pero en general el sistema debería haber podido reconocer en tales condiciones (por supuesto, peor).
Aproximación a la solución: la tarea se descompuso en 2 tareas + estructura de base de datos.
Memoria de corto plazo
Un servicio separado, donde se lleva a cabo principalmente el proceso en tiempo real, en la entrada es un marco de la cámara (de hecho, otro servicio), en la salida: una solicitud http con un vector X de 512 dimensiones normalizado (face-id) y algunos metadatos, por ejemplo, marca de tiempo.
Hay muchas soluciones interesantes en el campo de la lógica y la optimización dentro de él, pero eso es todo; por ahora todo ...
Memoria a largo plazo
Un servicio separado, donde los requisitos en tiempo real no son agudos, pero en algunos casos es importante (por ejemplo, una persona de una lista de detención). En general, nos limitamos a 3 segundos para procesar.
A la entrada del servicio: http de la memoria a corto plazo con un vector de 512 dimensiones en el interior; a la salida - la identificación del visitante.
Los primeros pensamientos son obvios, la solución al problema es bastante simple: obtuve http → fui a la base de datos, tomé lo que tengo → lo comparé con el relleno http, si hay uno, entonces es; si no, entonces nuevo.
Las ventajas de dicha solución son innumerables, pero solo una desventaja: no funciona.
El problema se solucionó, aunque seguimos el camino de los samuráis, probando varios enfoques, mirando periódicamente la inmensidad de Internet. En general, la decisión resultó ser moderadamente lacónica. El concepto es bastante simple y se basa en la agrupación:
- Cada vector (a-vector) pertenecerá a algún Usuario; cada grupo (no más de M vectores, fuera de la caja M = 30) pertenece a algún Usuario. Si el vector a pertenece al grupo A no es un hecho. Los vectores en el grupo definen la interacción del grupo, los vectores en el usuario definen solo el historial del usuario.
- Cada grupo tendrá un centroide (de hecho, un vector A) y su propio radio (en lo sucesivo, rango) de interacción con otros vectores o grupos.
- El centroide y el rango serán una función de grupo, no estática.
- La proximidad de los vectores está determinada por la distancia euclidiana al cuadrado (en casos especiales, de lo contrario). Si bien hay algunos otros métodos decentes aquí, simplemente nos detuvimos allí.
Nota: desde Usamos vectores normalizados, la distancia entre ellos se garantizó de 0 a 2. A continuación, sobre el algoritmo para implementar el concepto.
# 1 El círculo de sospechosos. Centroide como función hash
El vector X obtenido de la memoria a corto plazo se compara con los centroides del clúster (vector A) disponibles en la base de datos para la proximidad, los distantes, donde se descartaron el rango [X, A]> 1 -. Si no queda nadie, se crea un nuevo clúster.
A continuación, se busca el mínimo entre el vector X y todos los vectores a restantes (rango_min [X, a])
# 2 Propiedades únicas del cluster. Entidad autorreguladora
Se calcula el propio range_A del clúster, cuyo vector está más cercano al vector X. Aquí usamos la función lineal inversa del número de vectores (N) que ya están en este grupo (const * (1 - N / 2M)); fuera de la caja const = 0,67).
# 3 Validación y malentendidos. Si no es alguien, ¿entonces quién?
Si range_A> min_range [X, a], entonces el vector X se marca como perteneciente al grupo A. Si no, entonces ... Oh ... Esto es algo similar a la descripción del modelo matemático del malentendido.
Decidimos que en este caso crearemos un nuevo clúster, cometiendo deliberadamente un error del primer tipo "Objetivo perdido".
# 4 Entrenamiento adicional. Cómo los números forman signos
La experiencia subjetiva es cuando los datos se convierten en una herramienta. Lo reconocimos antes, pero posiblemente con un error. ¿Debo confiar en el vector X para usarlo en el próximo partido? ¡Comprobación! El vector X debe:
- estar lo suficientemente cerca del centroide A (rango_A> rango [X, A])
- sea útil y diverso, porque por un lado minimizamos el riesgo de errores, por otro, tampoco necesitamos copias (Config_Max [0.35]> rango [X, a]> Config_Max [0.125]). Por lo tanto, las configuraciones determinan la velocidad y la corrección del "aprendizaje".
Cumpliendo estas condiciones, el vector X se incluye en el grupo A (antes de eso, simplemente pertenecía al usuario). Si hay más vectores en el grupo, entonces eliminamos el más central (rango_mín [A, a]) - introduce la menor variedad y es solo una función de los demás; además, el centroide ya está involucrado en el emparejamiento.
# 5 Trabaja en errores. Convertimos desventajas en ventajas
En cada elección difícil, dimos un paso hacia el error "Falta objetivo": creamos un nuevo clúster y usuario. Es hora de volver a visitarlos ... todos. Después del n. ° 4 tenemos un grupo A modificado. A continuación, recalculamos su centroide (vector A) y buscamos la distancia mínima a todos los centroides disponibles en nuestro espacio de 512 dimensiones. En este caso, la distancia se considera más difícil, pero ahora esto no es tan importante. Cuando la distancia min_range [A, B] es menor que un cierto valor (fuera de la caja range_unity = 0.25) combinamos dos conjuntos, calculamos un nuevo centroide y nos deshacemos de los vectores menos "útiles" si hay demasiados.
En otras palabras: si hay más de 2 grupos, de hecho, pertenecientes al mismo Usuario, luego, después de una serie de detecciones, se acercarán y se fusionarán en uno junto con sus historias.
# 6 Funciones combinatorias. Cuando el coche ... piensa!?
Vale la pena definir un nuevo término aquí en este artículo. Un vector fantasma es un vector que se obtuvo no como resultado de la actividad de la memoria a corto plazo, sino como resultado de una función sobre N vectores del grupo (a1, a2, a3, a4 ...). Por supuesto, los vectores obtenidos de esta manera se almacenan y contabilizan por separado y no representan ningún valor hasta que, como resultado del emparejamiento, se determinan como los más cercanos (ver # 3). El principal beneficio de los vectores fantasma es acelerar el aprendizaje temprano de un grupo .
El sistema ya está en producción. El resultado se obtuvo con datos reales fuera del entorno de prueba para más de 5000 usuarios; Allí también se notaron un montón de "debilidades", que fueron reforzadas y tomadas en cuenta en este texto.
Curiosamente, este enfoque no tiene configuraciones de usuario y todo el trabajo no está controlado de ninguna manera, todo es completamente autónomo . Además, el análisis de series de tiempo le permite clasificar al Usuario en diferentes categorías de manera similar, creando así asociaciones. Así es como se resolvió la pregunta: quiénes son los empleados y quiénes son los visitantes.
La función del usuario del sistema es simple: debe verificar periódicamente su correo o la interfaz del sistema para obtener nuevos informes de actividad.
Resultado
El valor de proximidad para el reconocimiento basado en la memoria a largo plazo es ~ 0.12-0.25 para un grupo moderadamente entrenado (contiene 6-15 a-vectores). Además, el aprendizaje se ralentiza debido a un aumento en la probabilidad de "copias vectoriales", pero a largo plazo la proximidad tiende a valores de ~ 0,04-0,12, cuando el grupo ya contiene más de 20 vectores a. Tenga en cuenta que dentro de la memoria a corto plazo, de fotograma a fotograma, el mismo parámetro tiene un valor de ~ 0,5-1,2, que suena algo así como: "Una persona se parece más a sí misma con gafas hace 2 años que hace 100 ms". Estas oportunidades se abren mediante el uso de agrupaciones en la memoria a largo plazo .
Enigma
Una de las pruebas resultó en una observación interesante.
Condiciones iniciales:
- Un sistema de videovigilancia absolutamente idéntico con configuraciones absolutamente idénticas se implementa en dos PC absolutamente idénticos. Están conectados a una sola cámara ip, ubicada correctamente, de acuerdo con los TOR.
Actuar:
- Los sistemas se inician al mismo tiempo y se dejan solos durante una semana con todos los algoritmos en funcionamiento. El tráfico coincide con el tráfico normal sin cambios.
Resultado:
- El número de usuarios, clústeres y vectores a creados es el mismo, pero los centroides son diferentes, no significativamente, pero sí diferentes. La pregunta es ¿por qué? Quién sabe - escribe en los comentarios o aquí )
Es una lástima que no pueda escribir sobre muchas cosas aquí, pero tal vez pueda describir algo con el mismo detalle en otro artículo. Quizás todo esto ya haya sido descrito en algún maravilloso manual, pero a mi pesar, nunca encontré ninguno.
En conclusión, diré que es muy interesante observar desde el interior cómo un sistema de IA autónomo clasifica el espacio circundante, implementando diversas características inherentes al mismo en el camino. La gente no nota muchas cosas debido a la experiencia acumulada de percepción (paso # 4).
Realmente espero que este artículo sea de utilidad para cualquiera en su proyecto.