Acceso multiprocesador Intel Neural Computer Stick a través de REST

Problema de una sola tarea


En la última serie, puse Intel Neural Computer Stick 2 en el tanque y arrojé todos los cálculos de la red neuronal, abandonando Tensorflow y OpenCV-DNN.

Hubo un problema que ya encontré en ese momento: la incapacidad de trabajar con NCS desde varios procesos simultáneamente. Entonces no fue crítico, pero ahora es el momento de resolverlo.

Al intentar cargar un modelo del segundo proceso, OpenVino comenzó a jurar:

E: [ncAPI] [ 926029] resetAll:348 Failed to connect to stalled device, rc: X_LINK_ERROR E: [ncAPI] [ 933282] ncDeviceOpen:672 Failed to find suitable device, rc: X_LINK_DEVICE_NOT_FOUND 

Al buscar en el foro de soporte de Intel, se encontró un problema similar.

Desde allí fuimos transferidos a la documentación donde se establece claramente:

El dispositivo único no se puede compartir entre múltiples procesos.

En este experimento, puede minimizar y comenzar a hacer acceso multiproceso.

Servicio NCS


Es completamente lógico colocar trabajo directo con NCS en un servicio separado y distribuir API a todos los clientes a través de los cuales trabajarán.

En general, se suponía que este era un tema sobre el robot y sus nuevos logros en términos de redes neuronales. Pero resultó que el material en la API de NCS está bastante atraído por un artículo separado.

API NCS


En un nivel bajo, la API de NCS es muy simple:
- modelo de carga
- iniciar cálculo
- obtener una lista de modelos
- obtener propiedades del modelo

Si todo es inequívoco al cargar el modelo, entonces el escape de cálculo es un tensor sensible al contexto que el cliente puede no necesitar todo.

La obtención de una lista de modelos también es bastante transparente, y de las propiedades de la dimensión del tensor de entrada viene inmediatamente a la mente; en términos humanos, esto significa que sería bueno ajustar las imágenes de antemano a la configuración de la red.

Además, un nivel bajo es bueno, pero si admite operaciones especializadas, simplifica la lógica y los datos.

Por lo tanto, además de la base, hay una tarea para admitir la API para la clasificación, detección y segmentación.

Desafortunadamente, los modelos de segmentación más interesantes no son compatibles con el NCS, por lo que debe limitarse a los más simples, con el camino y el marcado.

Cualquiera de estas operaciones utiliza el cálculo básico del modelo, pero difieren en la interpretación del tensor de salida.

Interfaz principal


Entonces, la interfaz principal incluye métodos:

  • POST: / load - carga el modelo
  • POST: / unload / $ model: elimina el modelo (del servicio, es imposible eliminarlo del dispositivo)
  • OBTENGA: / list - obtenga una lista de modelos
  • GET: / input / shape / $ model - descubre la dimensión del tensor de entrada
  • POST: / inference / file / $ model: haga un cálculo con datos de la memoria
  • POST: / inference / path / $ model: haga un cálculo con datos en el sistema de archivos

Aquí hay dos palabras sobre los datos de la memoria y el sistema de archivos:

Si el servicio NCS y su usuario se están ejecutando en la misma Raspberry, entonces tiene sentido ahorrar en la transferencia de la imagen y, en su lugar, transferir la ruta para que el servicio mismo lea el archivo.
Si la imagen ya está en la memoria (o no existe en el sistema de archivos), la transferimos directamente desde allí.

Las pruebas muestran que la transferencia de bytes desde la memoria es significativamente más lenta (medición realizada para 1000 intentos):

De memoria: 87.5 segundos
Ruta del archivo: 63.3150 segundos

Sin embargo, estas dos opciones son compatibles con cualquier método, tanto para el cálculo general como para los casos especiales a continuación.

En general, el método de inferencia toma una imagen en forma de matriz numpy como entrada y produce un tensor en el mismo formato.
Cómo interpretar el escape ya es un problema del cliente.
Para facilitar esta tarea, el servicio admite métodos especializados que extraen información significativa en forma humana del tensor de salida.

Clasificación


Para la clasificación, creamos un método REST separado, que convierte el tensor de salida en un conjunto de pares (clase, puntaje).

 def get_class_tensor(data): ret = [] thr = 0.01 while(True): cls = np.argmax(data) if data[cls] < thr: break; logging.debug(("Class", cls, "score", data[cls])) c = {"class" : int(cls), "score" : int(100 * data[cls])} data[cls] = 0 ret.append(c) return ret def classify(model_id, img): rc, out = run_inference(model_id, img) if not rc: return rc, out return True, get_class_tensor(out) 

Como en el caso de la salida normal, se admiten dos métodos: a través de un archivo en la memoria y una ruta en el disco.

  • POST: / classify / file / $ model
  • POST: / classify / path / $ model

Detección


El tensor de salida del detector contiene un conjunto (clase, probabilidad, coordenadas normalizadas) y parece bastante engorroso.

Lo convertimos en una forma comprensible, al tiempo que eliminamos opciones poco probables:

 def get_detect_from_tensor(t, rows, cols): score = int(100 * t[2]) cls = int(t[1]) left = int(t[3] * cols) top = int(t[4] * rows) right = int(t[5] * cols) bottom = int(t[6] * rows) return {"class" : cls, "score" : score, "x" : left, "y" : top, "w" : (right - left), "h" : (bottom - top)} def build_detection(data, thr, rows, cols): T = {} for t in data: score = t[2] if score > thr: cls = int(t[1]) if cls not in T: T[cls] = get_detect_from_tensor(t, rows, cols) else: a = T[cls] if a["score"] < score: T[cls] = get_detect_from_tensor(t, rows, cols) return list(T.values()) def detect(model_id, img): rc, out = run_inference(model_id, img) if not rc: return rc, out rows, cols = img.shape[:2] return True, build_detection(out[0], 0.01, rows, cols) 

Como de costumbre, ambos métodos son compatibles:

  • POST: / detect / file / $ model
  • POST: / detect / path / $ model

Segmentación


El tensor de segmentación contiene probabilidades por clase e incluso en la dimensión de la red neuronal.
Convierta esto simplemente en una máscara de clase:

 def segment(model_id, img): rc, out = run_inference(model_id, img) if not rc: return rc, out out = np.argmax(out, axis=0) out = cv.resize(out, (img.shape[1], img.shape[0]),interpolation=cv.INTER_NEAREST) return True, out 

  • POST: / segmento / archivo / $ modelo
  • POST: / segmento / ruta / $ modelo

Conclusión


Como ya se mencionó, originalmente planeé hablar sobre el servicio en uno de los capítulos del artículo sobre su uso, pero resultó que el volumen está llegando a un documento separado.

Nuevamente, uso el servicio en Raspberry Pi, pero se puede ejecutar en cualquier plataforma que tenga Python y OpenVino con NCS.

Referencias


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


All Articles