教程:创建 Windows 机器学习 UWP 应用程序 (C#)
在本教程中,我们将构建一个简单的通用 Windows 平台应用程序,它使用训练后的机器学习模型来识别用户绘制的数字。 本教程主要介绍如何在 UWP 应用程序中加载和使用 Windows ML。
下面的视频介绍了本教程所基于的示例。
如果你只想查看已完成教程的代码,可以在 WinML GitHub 存储库上找到它。 C++/CX 中也提供了该代码。
先决条件
- Windows 10(版本 1809 或更高版本)
- Windows 10 SDK(版本 17763 或更高版本)
- Visual Studio 2019(或 Visual Studio 2017 版本 15.7.4 或更高版本)
- 适用于 Visual Studio 2019 或 2017 的 Windows 机器学习代码生成器扩展
- 一些基本的 UWP 和 C# 知识
1. 在 Visual Studio 中打开项目
从 GitHub 下载项目后,启动 Visual Studio 并打开 MNIST_Demo.sln 文件(它应当位于<存储库路径>\Windows-Machine-Learning\Samples\MNIST\Tutorial\cs)。 如果解决方案显示为不可用,则你需要在“解决方案资源管理器”右键单击该项目,然后选择“重新加载项目”。
我们提供了已实现 XAML 控件和事件的模板,包括:
- 一个 InkCanvas,用于绘制数字。
- 按钮,用于解释数字并清除画布。
- 帮助程序例程,用于将 InkCanvas 输出转换为 VideoFrame。
在“解决方案资源管理器”内,项目有三个主要代码文件:
- MainPage.xaml - 我们的所有 XAML 代码,用来为 InkCanvas、按钮和标签创建 UI。
- MainPage.xaml.cs - 我们的应用程序代码所在位置。
- Helper.cs - 帮助程序例程,用于裁剪和转换图像格式。
2. 生成并运行该项目
在 Visual Studio 工具栏中,将“解决方案平台”更改为“x64”(如果你的设备是 64 位的)或“x86”(如果你的设备是 32 位的)以在你的本地计算机上运行该项目。 (可在 Windows 设置应用中检查:“系统”>“关于”>“设备规格”>“系统类型”。)
若要打开项目,请单击工具栏上的“开始调试”按钮,或者按 F5。 应用程序应显示用户可写入数字的 InkCanvas,一个用来解释数字的“识别”按钮,一个将经过解释的数字显示为文本的空标签字段,以及一个用来清除 InkCanvas 内容的“清除数字”按钮。
注意
如果没有生成项目,则可能需要更改项目的部署目标版本。 在“解决方案资源管理器”中右键单击该项目并选择“属性”。 在“应用程序”选项卡中,设置与你的操作系统和 SDK 匹配的“目标版本”和“最低版本”。
注意
如果收到指出应用程序已安装的警告,只需选择“是”以继续进行部署。 如果 Visual Studio 仍然不工作,则可能需要将其关闭并重新打开。
3. 下载模型
接下来,让我们来获取要添加到应用程序中的机器学习模型。 本教程中将使用已预先训练的 MNIST 模型,它是使用 Microsoft Cognitive Toolkit (CNTK) 训练的,并已导出为 ONNX 格式。
MNIST 模型已包含在你的“资产”文件夹中,你将需要将它作为现有项添加到应用程序中。 你还可以从 GitHub 上的 ONNX Model Zoo 下载已预先训练的模型。
4. 添加模型
在“解决方案资源管理器”中右键单击“资产”文件夹,然后选择“添加”>“现有的项”。 将文件选取器指向 ONNX 模型的位置,然后单击“添加”。
该项目现在应该有两个新文件:
- mnist.onnx - 你的已训练模型。
- mnist.cs - Windows ML 生成的代码。
要确保在我们编译应用程序时模型能够生成,请右键单击 mnist.onnx 文件,然后选择“属性”。 对于“生成操作”,选择“内容”。
现在,让我们来看看 mnist.cs 文件中新生成的代码。 我们有三个类:
- MNISTModel 创建机器学习模型表示,在系统默认设备上创建一个会话,将特定输入和输出绑定到模型,并异步评估模型。
- mnistInput 初始化模型所需的输入类型。 在此示例中,输入需要 ImageFeatureValue。
- mnistOutput 初始化模型将输出的类型。 在本例中,输出将是一个名为 Plus214_Output_0 且类型为 TensorFloat 的列表。
现在我们将使用这些类在我们的项目中加载、绑定并评估模型。
5. 加载、绑定并评估模型
对于 Windows ML 应用程序,我们想要遵循的模式是:加载 > 绑定 > 评估。
- 加载机器学习模型。
- 将输入和输出绑定到模型。
- 评估模型并查看结果。
我们将使用在 mnist.cs 中生成的接口代码在我们的应用程序中加载、绑定并评估模型。
首先,在 MainPage.xaml.cs 中实例化模型、输入和输出。 将下列成员变量添加到 MainPage 类:
private mnistModel ModelGen;
private mnistInput ModelInput = new mnistInput();
private mnistOutput ModelOutput;
然后,在 LoadModelAsync 中加载模型。 在使用模型的任何方法之前(也就是说,触发 MainPage 的 Loaded 事件时、调用 OnNavigatedTo 的 override 时,或者在调用 recognizeButton_Click 之前的任何地方),都应当调用此方法。 mnistModel 类表示 MNIST 模型并在系统默认设备上创建会话。 为加载该模型,我们将调用 CreateFromStreamAsync 方法,并将 ONNX 文件作为参数传入。
private async Task LoadModelAsync()
{
// Load a machine learning model
StorageFile modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Assets/mnist.onnx"));
ModelGen = await mnistModel.CreateFromStreamAsync(modelFile as IRandomAccessStreamReference);
}
注意
如果 IRandomAccessStreamReference 下出现了红色下划线,则需要包括其命名空间。 将光标置于其上,按 Ctrl + .,从下拉菜单中选择“使用 Windows.Storage.Streams”。
接下来,我们要将输入和输出绑定到模型。 生成的代码还包括 mnistInput 和 mnistOutput 包装类。 mnistInput 类表示模型的预期输入,mnistOutput 类表示模型的预期输出。
若要初始化模型的输入对象,请调用 mnistInput 类构造函数,传入你的应用程序数据,并确保输入数据与模型预期的输入类型匹配。 mnistInput 类预期的是 ImageFeatureValue,因此我们使用一个帮助程序方法来获取 ImageFeatureValue 作为输入。
使用我们在 helper.cs 中包含的帮助程序函数,我们将复制 InkCanvas 的内容、将其转换为 ImageFeatureValue 类型,然后将其绑定到模型。
private async void recognizeButton_Click(object sender, RoutedEventArgs e)
{
// Bind model input with contents from InkCanvas
VideoFrame vf = await helper.GetHandWrittenImage(inkGrid);
ModelInput.Input3 = ImageFeatureValue.CreateFromVideoFrame(vf);
}
对于输出,我们只需使用指定输入调用 EvaluateAsync。 在初始化你的输入后,调用模型的 EvaluateAsync 方法基于输入数据来评估你的模型。 EvaluateAsync 将你的输入和输出绑定到模型对象,并基于输入来评估模型。
由于模型返回一个输出张量,因此,我们首先要将其转换为一个友好的数据类型,然后解析返回的列表,以确定哪一个数字的概率最高并显示该数字。
private async void recognizeButton_Click(object sender, RoutedEventArgs e)
{
// Bind model input with contents from InkCanvas
VideoFrame vf = await helper.GetHandWrittenImage(inkGrid);
ModelInput.Input3 = ImageFeatureValue.CreateFromVideoFrame(vf);
// Evaluate the model
ModelOutput = await ModelGen.EvaluateAsync(ModelInput);
// Convert output to datatype
IReadOnlyList<float> vectorImage = ModelOutput.Plus214_Output_0.GetAsVectorView();
IList<float> imageList = vectorImage.ToList();
// Query to check for highest probability digit
var maxIndex = imageList.IndexOf(imageList.Max());
// Display the results
numberLabel.Text = maxIndex.ToString();
}
最后,我们要清除 InkCanvas,以便用户绘制另一个数字。
private void clearButton_Click(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.StrokeContainer.Clear();
numberLabel.Text = "";
}
6. 启动应用程序
生成并启动该应用程序后(按 F5),我们将能够识别在 InkCanvas 上绘制的数字。
大功告成,你已开发出你的第一个 Windows ML 应用程序! 有关演示了如何使用 Windows ML 的更多示例,请查看 GitHub 上的 Windows-Machine-Learning 存储库。
注意
使用以下资源可获取有关 Windows ML 的帮助:
- 若要提出或回答有关 Windows ML 的技术问题,请在 Stack Overflow 上使用 windows-machine-learning 标记。
- 若要报告 bug,请在 GitHub 上提交问题。