构建自己的计算机视觉云服务






4.85/5 (10投票s)
使用 ASP.NET Core、Tensorflow 和 Azure 云构建一个简单的机器学习服务
目录
引言
亚马逊、谷歌和微软等顶级云平台供应商提供强大且易于使用的 REST API,用于图像处理、视频分析、语音识别和其他高级算法。它们允许开发人员在不深入了解数据科学的情况下,使他们的应用程序更加智能。
然而,这些 API 是通用目的的,不能根据自己的需求进行定制,所以我一直在想如何构建自己的简单计算机视觉服务,它仍然是公开可用的,但能给我更多的灵活性和控制权。
本文不试图解释计算机视觉算法是如何工作的,而是介绍开发人员如何使用现代技术构建和部署自己的智能服务。
计算机视觉服务的作用
计算机视觉服务的理念非常简单:它是一个公开可用的 REST 服务,接收一张图片,并能在图片上定位和识别一个或多个对象。
接下来,我们将深入探讨该服务的工作原理以及如何训练底层的机器学习模型。此外,您可以通过点击以下按钮将计算机视觉服务发布到您的 Azure 帐户中。
它是如何工作的?
让我们简要讨论一下我们所需的技术栈以及它们将如何被使用。
计算机视觉服务核心是一个对象检测TensorFlow模型,由frozen_inference_graph.pb文件表示。该文件包含模型的图定义和元数据。图定义以各个操作之间的依赖关系表示您的计算。元数据包含继续训练、执行评估或在先前训练过的图上运行推理所需的信息。对象检测图有一个“image_tensor
”输入张量——任何将检测对象的图像,以及四个输出张量:detection_boxes
——检测到对象的边界,detection_scores
——正确性置信度百分比,detection_classes
——检测到对象的类别(汽车、人、动物等),num_detections
——批次中每张图像的有效框数量。此外,对象检测图可以加载到TensorFlow框架中(这将在“选择机器学习模型”部分详细介绍)。一旦TensorFlow设置并模型训练完成,我们就可以运行执行图并处理输入图像。
我想将计算机视觉公开为 REST 服务,并使用我著名的编程语言 C# 来创建它,所以我选择了 ASP.NET Core 2.0 作为 Web 框架,这样我就可以在 Linux 上运行 TensorFlow 和 ASP.NET Core 服务。这非常重要,因为许多 TensorFlow 模型在 Linux 上的文档更完善。
现在,我需要考虑如何在托管的 .NET 代码和非托管的 Tensorflow API 之间搭建一座桥梁,以使服务正常工作。为此,我将使用 TensorFlowSharp 库——用于 C# 的托管 TensorFlow API(请参阅“从 C# 使用 Tensorflow 模型”部分)。在为 .NET Standard 2.0 编译 TensorFlowSharp 后,我将能够在 Linux 上的 ASP.NET Core 2.0 中使用它。
所以最后,我将把计算机视觉服务发布到 Azure Linux VM 上,使 REST API 公开可用。
计算机视觉 REST 服务
选择 Tensorflow 模型
如前所述,该服务的核心是专门用于检测图像上对象的 TensorFlow 模型。我将使用 TensorFlow 研究中的对象检测模型。我将在本文后续部分对自定义数据集进行模型训练,但您也可以使用tensorflow 检测模型动物园中的预训练模型之一。
从 C# 使用 TensorFlow 模型
以下代码使用 TensorFlowSharp 绑定将模型导入 TensorFlow 并在图像上检测对象
public static void Main<T> (string [] args, ILogger<T> logger)
{
options.Parse (args);
if (_catalogPath == null) {
_catalogPath = DownloadDefaultTexts (_currentDir);
}
if (_modelPath == null) {
_modelPath = DownloadDefaultModel (_currentDir);
}
_catalog = CatalogUtil.ReadCatalogItems (_catalogPath);
var fileTuples = new List<(string input, string output)> ()
{ (_input, _output) };
string modelFile = _modelPath;
using (var graph = new TFGraph ())
{
// imports model from the disk into tensorflow framework
var model = File.ReadAllBytes (modelFile);
graph.Import (new TFBuffer (model));
using (var session = new TFSession (graph)) {
foreach (var tuple in fileTuples) {
// converts input image to a tensor
var tensor = ImageUtil.CreateTensorFromImageFile
(tuple.input, TFDataType.UInt8);
var runner = session.GetRunner ();
// specifies input and output tensors
runner
.AddInput (graph ["image_tensor"] [0], tensor)
.Fetch (graph ["detection_boxes"] [0],
graph ["detection_scores"] [0],
graph ["detection_classes"] [0],
graph ["num_detections"] [0]);
var output = runner.Run ();
// fetches graph execution results.
// They can be used for highlighting
// detected objects on the picture
var boxes = (float [,,])output [0].GetValue (jagged: false);
var scores = (float [,])output [1].GetValue (jagged: false);
var classes = (float [,])output [2].GetValue (jagged: false);
var num = (float [])output [3].GetValue (jagged: false);
DrawBoxes (boxes,
scores,
classes,
tuple.input,
tuple.output,
MIN_SCORE_FOR_OBJECT_HIGHLIGHTING);
}
}
}
}
公开 REST API
这是 ASP.NET Core 2.0 控制器的样子
[Route("api/[controller]")]
public class ObjectDetectionController : Controller
{
private const string TrainedModelFileName = "frozen_inference_graph.pb";
private const string CatalogFileName = "mscoco_label_map.pbtxt";
private ILogger<objectdetectioncontroller> _logger;
private readonly IHostingEnvironment _hostingEnvironment;
public ObjectDetectionController(ILogger<objectdetectioncontroller> logger,
IHostingEnvironment hostingEnvironment)
{
_logger = logger;
_hostingEnvironment = hostingEnvironment;
}
[HttpGet("{id}")]
public IActionResult GetProcessedImageById(string id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
var image = System.IO.File.OpenRead($"test_images/{id}_detected.jpg");
return File(image, "image/jpeg");
}
[HttpPost]
public (string, string) DetectObjects([FromBody]string imageAsString)
{
if (imageAsString == null)
throw new ArgumentNullException(nameof(imageAsString));
// generates input and processed file names
string id = Guid.NewGuid().ToString("N");
string inputImage = GetSafeFilename($"{id}.jpg");
string outputImage = GetSafeFilename($"{id}_detected.jpg");
string inputImagePath = Path.Combine
(_hostingEnvironment.ContentRootPath, "test_images", inputImage);
string outputImagePath = Path.Combine
(_hostingEnvironment.ContentRootPath, "test_images", outputImage);
// saves input image on the disk
SaveImage(imageAsString, inputImagePath);
// runs TensorFlow and detect objects on the image
ExampleObjectDetection.Program.Main(new string[] {
$@"--input_image={inputImagePath}",
$@"--output_image={outputImagePath}" ,
$@"--catalog={Path.Combine
(_hostingEnvironment.ContentRootPath,CatalogFileName)}" ,
$@"--model={Path.Combine
(_hostingEnvironment.ContentRootPath,TrainedModelFileName)}" ,
},
_logger);
// returns processed image and preview url
string detectedImage = Convert.ToBase64String
(System.IO.File.ReadAllBytes(outputImagePath));
var previewUrl = UriHelper.BuildAbsolute
(Request.Scheme, Request.Host, path:
new Microsoft.AspNetCore.Http.PathString
($"/api/objectdetection/{id}"));
return (previewUrl, detectedImage);
}
private static void SaveImage(string imgStr, string imgPath)
{
byte[] imageBytes = Convert.FromBase64String(imgStr);
System.IO.File.WriteAllBytes(imgPath, imageBytes);
}
private string GetSafeFilename(string filename)
{
return string.Join("_", filename.Split
(Path.GetInvalidFileNameChars()));
}
}
控制器公开了两个 REST 方法
DetectObjects()
和GetProcessedImageById()
DetectObjects()
API 接受 base64
编码图像并返回包含检测到对象的图像 URL。GetProcessedImageById()
允许预览处理后的图像。
部署到云端
在此阶段,我可以在本地机器上运行该服务,现在是时候将其发布到 Azure 了。我已经准备好带有几个脚本的 ARM 模板,这些脚本将
- 部署 Linux 虚拟机
- 安装 TensorFlow 和 .NET Core
- 从 GitHub 拉取源代码,构建,并使用 nginx 和 ASP.NET Core 公开 REST API
VM 创建后,即可通过 RDP 或 SSH 访问(参见如何在 Linux VM 上使用 SSH)。连接并检查服务是否成功部署,以及“objectdetection
”目录是否包含以下文件
让我们来试用一下
计算机视觉服务已发布,我想通过发送一些图片进行处理来试用它。首先,我将图片编码为 base64 字符串
,这可以通过几行 C# 代码完成,甚至只需使用一些在线服务,例如https://www.base64-image.de。
然后,使用 Postman(或任何其他实用程序)向计算机视觉服务发送 HTTP 请求。HTTP 请求应包含以下标头
Accept:text/plain
Content-Type:text/plain
以及正文中 base64 编码的图像。
作为响应,我收到处理后的图像的 URL 和 base64 编码的处理后图像。通过点击 URL,我可以加载它
故障排除
检查日志以进行故障排除。它们位于/var/log/dotnettest.err.log和/var/log/dotnettest.out.log文件中。
训练你自己的模型
在本节中,我们将讨论如何训练对象检测模型。
部署用于训练的虚拟机
为了训练对象检测模型,我们需要部署一台特殊的虚拟机。训练需要大量的计算,可能需要数小时。为了加快训练过程,我们将在 Azure 中使用基于 GPU 的虚拟机(在此处查看可用大小列表:此处),并在几个 GPU 单元上并行化计算。我已经为部署对象检测训练虚拟机准备了 ARM 模板。
步骤 1 - 在 Azure 上部署 GPU VM
点击“部署”并填写表单,在您的 Azure 帐户上部署训练 Linux VM
步骤 2 - 下载 CUDA 和 cuDNN
CUDA 是 NVIDIA 开发的一种并行计算平台和编程模型,用于在图形处理单元 (GPU) 上进行通用计算。
- 下载以下 CUDA 版本:libcudnn5_5.1.10-1+cuda8.0_amd64.deb
好的,那么什么是 cuDNN?NVIDIA CUDA® 深度神经网络库 (cuDNN) 是一个用于深度神经网络的 GPU 加速原语库。
- 下载以下 cuDNN 版本:https://developer.nvidia.com/rdp/cudnn-download
这将需要您在 NVIDIA 网站上注册
步骤 3 - 将 CUDA 和 CUDNN 上传到训练虚拟机
您需要从 Windows 命令行执行以下命令,将 CUDA 和 cuDNN 安装文件从您的本地机器(我假设它们位于“Downloads”文件夹中)上传到训练虚拟机
pscp %UserProfile%\Downloads\libcudnn5_5.1.10-1+cuda8.0_amd64.deb
testadmin@52.174.127.204:/home/testadmin/
pscp %UserProfile%\Downloads\cudnn-8.0-linux-x64-v6.0.solitairetheme8
testadmin@52.174.127.204:/home/testadmin/
步骤 4 - 安装 NVIDIA 驱动
现在,我们可以安装 NVIDIA 驱动程序。我准备了一个名为“install_driver.sh”的 shell 脚本,并已上传到训练虚拟机。您需要通过 SSH 或 RDP 连接到您的训练虚拟机,并使用以下命令启动安装
sh install_driver.sh
请耐心等待,这需要一些时间。驱动程序安装后,您的虚拟机将重新启动,因此您需要再次通过 SSH 或 RDP 连接。
步骤 5 - 安装 CUDA 和 cuDNN
重新启动后,我们需要安装之前上传的 CUDA 和 cuDNN。为此,请执行以下脚本
sudo dpkg -i libcudnn5_5.1.10-1+cuda8.0_amd64.deb
wget https://developer.download.nvidia.com/compute/cuda/
repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
sudo apt-get update
sudo apt-get install cuda-8-0
tar -zxvf cudnn-8.0-linux-x64-v6.0.solitairetheme8
cd cuda
sudo cp include/cudnn.h /usr/local/cuda/include
sudo cp lib64/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/lib64/libcudnn*
sudo apt-get -y install python3-tk
初始化环境变量
vim ~/.bashrc
此命令打开 vim 编辑器,您需要向下滚动到最底部并插入以下行
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64"
export CUDA_HOME=/usr/local/cuda
export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:$CUDA_HOME/lib"
export PATH="$CUDA_HOME/bin:$PATH"
你的命令行应该看起来像这样
按下“Esc”键,然后输入“:wq
”——这将把更改写入.bashrc文件并退出 vim。重新加载.bashrc
source ~/.bashrc
步骤 6 - 检查是否成功
完成所有步骤后,您可以检查您的机器是否已准备好训练神经网络。在命令行中输入以下命令
nvcc --version
cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2
这将打印已安装的 CUDA 和 cuDNN 版本。确保您的训练机器上安装了 CUDA v.8.0 和 cuDNN v.6,如下图所示
步骤 7 - 开始训练对象检测
在此步骤中,我们将训练我们的对象检测神经网络。我建议通过 RDP 连接到训练用的 Ubuntu VM,因为您需要启动三个终端:一个启动 TensorBoard,一个启动训练过程,一个启动评估过程。下面我们将分别讨论它们。
首先,让我们启动 TensorBoard——一个功能强大的工具,它与 TensorFlow 一起提供,可以可视化 TensorFlow 图和学习过程。以下命令在您的训练虚拟机上启动 TensorBoard
sh start_tensorboard.sh
开始训练过程
. export_variables.sh
. train.sh
最后是评估
. export_variables.sh
. eval.sh
您应该能够看到这样的图片(左侧 - TensorBoard,中间 - 训练,右侧 - 评估过程)
TensorBoard
TensorBoard 是强大的工具,有助于理解、优化和调试神经网络训练。
TensorBoard
- 可视化神经网络图。我用它来找出哪些地方可以并行化,以及对底层神经网络结构的一般理解
- 通过在不同时间点显示张量的多个直方图可视化,可视化某个张量分布如何随时间变化
- 可视化学习过程。当您想了解何时需要停止训练过程时,这很有帮助(我们将在下一节中看到)
培训
训练过程需要训练数据——一组要通过机器学习算法的图像。训练数据必须包含正确答案,以便训练过程可以将算法的实际结果与您想要预测的结果进行比较。经过若干步骤后,训练过程会创建一个检查点,因为这可能需要很长时间,并且检查点允许在过程失败的情况下从上次检查点恢复训练。
通过观察 TensorBoard 上的 TotalLoss
图表来跟踪训练过程的进展是很有用的。TotalLoss
值越接近零,模型的预测效果越好。
我进行了 16k 步的训练,并得到了以下 TotalLoss
图表。正如你所看到的,在训练约 2k 步后,我得到了相当不错的预测结果,随后的迭代并没有显著改变预测结果
评估版
在训练过程的同时,您可以运行评估过程。评估可以不时进行,例如,每 500 步进行一次,用于测试和验证目的。
这是公交车图片评估的结果。您可以看到在训练过程中,对象检测的准确性是如何变化的
结论
让我们总结一下从本文中学到的知识。
- 本文演示了如何部署和定制自己的计算机视觉服务,但所描述的步骤也可用于构建基于 TensorFlow 模型的任何其他智能服务。可用 TensorFlow 模型的完整列表在此。
- .NET 开发者可以通过 TensorFlowSharp 绑定(封装了原生 TensorFlow API)在 Linux 上使用 TensorFlow。
- 如果您将密集的 TensorFlow 操作公开为 ASP.NET Core REST API,请考虑增加反向代理的默认超时时间。我使用 Nginx 作为反向代理,这里可以找到使用的配置。
- 考虑对 TensorFlow 模型的训练和评估使用不同的硬件配置。带有强大 GPU 的机器在图形任务和图像处理方面表现非常出色,因此它们可以让你非常快速地训练模型,但成本较高。一旦你训练好模型,就可以在更便宜的基于 CPU 的机器或机器集群上使用它。
- 您可以跳过模型训练,下载预训练模型来构建 PoC 项目或演示。此外,您还可以使用预训练模型的检查点作为对抗自有数据集进行训练的起点,这将为您节省时间。
- TensorFlow 模型的训练是计算密集型和耗时的操作,因此考虑在云端运行并将其并行化到不同的机器或单元上。我在 Azure 云的 NC12 VM 上进行训练,该 VM 由 NVIDIA Tesla K80 卡(带 2 个 GPU)提供支持,并在不同的 GPU 上并行化训练和评估过程。
- 使用Azure Resource Manager,我能够自动化大部分用于训练 TensorFlow 模型和托管 REST 服务的虚拟机部署操作。但是,显卡驱动的升级和 CUDA 的安装仍然需要通过 SSH 或 RDP 手动完成。
- TensorBoard 是一个强大的工具,用于可视化、优化和调试 TensorFlow 训练过程。
尽情让你的应用更智能吧!
参考文献
- Scott Hanselman:将 ASP.NET Core 网站发布到廉价的 Linux VM 主机
- GitHub: TensorFlowSharp - .NET bindings to the Tensorflow
- GitHub: Tensorflow 模型
- GitHub:Azure 快速启动模板
- Azure:GPU 优化虚拟机大小
- “现代卷积对象检测器的速度/精度权衡。” 黄 J, Rathod V, Sun C, Zhu M, Korattikara A, Fathi A, Fischer I, Wojna Z, Song Y, Guadarrama S, Murphy K, CVPR 2017
历史
- 2017 年 12 月 17 日:初始版本