通过REST进行多处理Intel神经计算机棒访问

单任务问题


在上一个系列中,我将Intel Neural Computer Stick 2放到了储罐上,并将所有神经网络计算都放到了储罐上,放弃了Tensorflow和OpenCV-DNN。

那时我已经遇到一个问题-无法同时使用多个进程的NCS。 那时并不重要,但是现在是时候解决了。

尝试从第二个过程加载模型时,OpenVino开始发誓:

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 

通过搜索英特尔支持论坛,发现了类似的问题。

从那里我们被转移到明确说明的文档中:

单个设备不能在多个进程之间共享。

在此实验中,您可以最小化并开始进行多进程访问。

NCS服务


将与NCS的直接工作放在单独的服务中,并将API分发给将通过它们工作的所有客户端是完全合乎逻辑的。

通常,这应该是关于机器人及其在神经网络方面的新成就的主题。 但是事实证明,NCS API上的材料很吸引人。

NCS API


在低层次上,NCS API非常简单:
-负载模型
-开始计算
-获取型号列表
-获取模型属性

如果在加载模型时一切都明确,则计算结果就是上下文相关的张量,客户端可能不需要全部。

获取模型列表也是相当透明的,并且输入张量的维数立即浮现在脑海中-用人的话来说,这意味着将图片事先调整为网络设置会很好。

另外,低级别是好的,但是如果您支持特殊操作,它将简化逻辑和数据。

因此,除了基础之外,还有一项任务支持API进行分类,检测和分段。

不幸的是,NCS 不支持最有趣的细分模型,因此您必须将自己限制在最简单的道路和标记上。

这些操作中的任何一个都使用模型的基本计算,但是它们在输出张量的解释上有所不同。

主界面


因此,主界面包括方法:

  • POST:/加载-加载模型
  • POST:/卸载/ $模型-删除模型(从服务中,无法从设备中删除)
  • GET:/列表-获取模型列表
  • GET:/输入/形状/ $模型-找出输入张量的尺寸
  • POST:/推论/文件/ $模型-使用内存中的数据进行计算
  • POST:/推论/路径/ $模型-使用文件系统中的数据进行计算

这是有关内存和文件系统中数据的两个词:

如果NCS服务及其用户在同一个Raspberry上运行,则有意义的是保存传输图片,而不是传输路径,以便服务本身读取文件。
如果图片已经在内存中(或文件系统中不存在),则我们直接从那里传输图片。

测试表明,从内存中传输字节的速度明显较慢(测量了1000次尝试):

内存中:87.5秒
档案路径:63.3150秒

但是,任何方法都支持这两个选项,无论是常规计算还是下面的特殊情况。

通常,推理方法以numpy数组形式的图片作为输入,并以相同格式生成张量。
如何解释废气已经是客户的问题。
为了简化此任务,该服务支持从输出张量中提取人类形式重要信息的专用方法。

分类


为了进行分类,我们创建了一个单独的REST方法,该方法将输出张量转换为一组对(类,得分)。

 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) 

与普通输出一样,支持两种方法-通过内存中的文件和磁盘上的路径。

  • POST:/分类/文件/ $模型
  • POST:/分类/路径/ $模型

侦测


检测器输出张量包含一个集合(类,概率,归一化坐标),看起来很麻烦。

我们将其转换为易于理解的形式,同时消除了不太可能的选择:

 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) 

与往常一样,两种方法都受支持:

  • POST:/检测/文件/ $模型
  • POST:/检测/路径/ $模型

细分


分割张量包含类的概率,甚至包含神经网络的维数。
简单地将其转换为类掩码:

 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:/段/文件/ $模型
  • POST:/段/路径/ $模型

结论


如前所述,我本来打算在文章的其中一章中讨论服务的使用,但是事实证明,该卷是在单独的文档中编写的。

再说一次,我在Raspberry Pi上使用了该服务,但是它可以在任何带有NCS的python和OpenVino的平台上运行。

参考文献


Source: https://habr.com/ru/post/zh-CN469265/


All Articles