使用 Windows ML API 在 Windows 应用中部署数据分析模型

在本教程的上一部分,你学习了如何以 ONNX 格式生成和导出模型。 现在,我们将演示如何将导出的模型嵌入 Windows 应用程序,并通过调用 Windows ML API 在本地在设备上运行它。

完成时,你将拥有一个正常工作的数据分析应用。

关于示例应用

在本教程的此步骤中,你将创建一个可以分析鸢尾花表格数据的应用。 该应用允许你添加具有所需输入信息的 Excel 文件,或手动输入输入参数 - 鸢尾花的花瓣长度和宽度(以 cm 为单位)。这些功能将由在上一部分生成的和训练的本地存储神经网络 ONNX 模型进行处理。 根据模型输出,应用将显示正确的鸢尾花类型。

在本部分中,我们将指导你完成该过程。

注释

如果选择使用预定义的代码示例,可以克隆解决方案文件。 克隆存储库,导航到此示例,然后使用 Visual Studio 打开 Iris Data Analysis.csproj 文件。跳到此页面的“启动应用程序”部分以查看它的使用情况

下面,我们将指导你如何创建应用并添加 Windows ML 代码。

创建 Windows ML Desktop (C#) 应用

若要创建可以工作的 Windows ML 应用,需要执行以下操作:

  • 加载机器学习模型。
  • 绑定模型的输入和输出。
  • 评估模型并显示有意义的结果。

还需要创建基本 UI,以提供更好的用户体验。

在 Visual Studio 中打开新项目

  1. 让我们开始吧。 打开 Visual Studio 并选择 Create a new project

创建新的 Visual Studio 项目

  1. 在搜索栏中,选择 C# 语言、 Windows 目标平台和 Dektop 项目类型。 选择 NUnit Test Project (.NET Core) 为项目类型,然后选择 next 打开项目的配置窗口。

创建新的 NUnit 测试项目应用

  1. 在配置窗口中执行以下操作:
  • 为项目命名。 在这里,我们称之为 鸢尾花数据分析
  • 选择项目的位置。
  • 如果使用的是 VS 2019,请确保选中 Create directory for solution
  • 如果使用的是 VS2017,请确保未勾选 Place solution and project in the same directory

按键 create 创建项目。 可能会弹出最低目标版本窗口。 请确保将最低版本设置为“Windows 10 版本 1809 (10.0;内部版本 17763)”或更高版本

  1. 创建项目后,导航到项目文件夹,打开资产文件夹 ,然后将 [….\DataClassifier\Assets] 文件复制到该位置Network.onnx

浏览项目解决方案

让我们探讨你的项目解决方案。

Visual Studio 在解决方案资源管理器中自动创建了多个 cs-code 文件。 MainPage.xaml 包含 GUI 的 XAML 代码,并 MainPage.xaml.cs 包含应用程序代码。 如果你以前创建过 UWP 应用程序,那么你对这些文件应该会非常熟悉。

虽然我们已将 Network.onnx 文件添加到资产文件夹,但需要将其正确添加到此项目。

  1. 右键单击解决方案资源管理器中的“资产”文件夹,然后选择 Add > Existing Item
  2. 导航到 Iris Data Analysis [….\Iris Data Analysis \Assets] 中的“资产”文件夹,找到之前您复制到其中的 Network.onnx model,然后选择 Add
  3. 若要确保编译应用程序时能够生成模型,请右键单击Network.onnx文件并选择Properties。 将 Build Action 设置为 Content

还需要创建新的 cs 代码类文件来容纳一些额外的机器学习代码,其中包括将调用 Windows ML API 的类和方法。

  1. 右键单击 Visual Studio 中的解决方案名称,然后选择 addnew item。 在打开的窗口中,选择 Class 并为其命名 - 此处,我们使用 IrisModel.cs。 新的类文件将在你的项目下提出上诉。

将新类文件添加到 VS 项目

创建机器学习代码

在此步骤中,我们将创建将调用 Windows 机器学习 API 的所有类和方法。 这样,就可以在项目中加载、绑定和评估 ONNX 机器学习模型。

  1. 双击该文件IrisModel.cs

  2. using 语句替换为以下内容,以获取对所需的所有 API 的访问权限。

using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.AI.MachineLearning;
using Windows.Storage;

初始化机器学习课程

我们需要添加多个类来帮助 IrisModel.cs 你与 Windows 机器学习 API 进行交互。

若要访问训练的机器学习模型,我们将使用该 LearningModel 类。 此类是命名空间的 Windows.AI.MachineLearning 一部分,表示经过训练的机器学习模型。 实例化后, LearningModel 它是用于与 Windows ML API 交互的主要对象。

若要评估学习模型,必须创建评估会话。 为此,请使用该 LearningModelSession 类。 此类用于评估机器学习模型,并将该模型绑定到一个设备上,然后运行和评估该模型。 使用此 API 创建会话时,还可以选择要执行模型的设备(默认值为 CPU)。

此外,还需要指定机器学习模型的输出标签。 稍后可以将这些标签连接到模型的预测输出。

注释

若要了解有关LearningModelLearningModelSession类的详细信息,请查看LearningModel 类文档LearningModelSession 类文档

  1. 将以下代码复制到 IrisModel.cs 该文件。
class IrisModel
    {
        private LearningModel _learning_model;
        private LearningModelSession _session;
        private String[] _labels = { "Iris-setosa", "Iris-versicolor", "Iris-virginica"};

加载模型

接下来,需要加载机器学习模型并创建一个会话,该会话将用于刚刚定义的类。 若要加载模型,将使用类的 LearningModel 多个静态方法 -在本例中,我们将使用 LoadFromStorageFileAsync,这样就可以从 ISorageFile 异步加载 ONNX 模型。

注释

若要详细了解如何加载模型,请查看 “加载模型”文档

  1. 将以下代码复制到 IrisModel.cs 该文件。
public async Task Initialize()
{
    // Load and create the model and session
    var modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Assets//Network.onnx"));
    _learning_model = await LearningModel.LoadFromStorageFileAsync(modelFile);
    _session = new LearningModelSession(_learning_model);
}

定义模型输入张量

现在,我们将根据模型要求定义正确的输入。 在上一部分中生成的网络模型有四个输入值。 每个输入值表示虹膜四个特征的可能大小:花瓣长度(厘米)、花瓣宽度(厘米)和花瓣宽度(厘米)。根据此输入,模型将返回最适合这些参数的虹膜类型。 你需要将输入值的大小限制为有效的逻辑值 - 在本教程中,我们将使用以下内容:

  • 萼片长度 - 1 厘米到 100 厘米
  • 花瓣宽度 – 1 厘米到 8 厘米
  • 花瓣长度 – 0.5 厘米到 10 厘米
  • 花瓣宽度 – 0.1 厘米到 5 厘米
  1. 将以下代码复制到 IrisModel.cs 该文件。
private float _sepal_length = 1.0f;
public float Sepal_Length
{
    get
    {
        return _sepal_length;
    }
    set
    {
        // validate range [1,10]
        if (value >= 1 && value <= 10)
        {
            _sepal_length = value;
        }
    }
}

private float _sepal_width = 1.0f;
public float Sepal_Width
{
    get
    {
        return _sepal_width;
    }
    set
    {
        // validate range [1, 8]
        if (value >= 1 && value <= 8)
        {
            _sepal_width = value;
        }
    }
}

private float _petal_length = 0.5f;
public float Petal_Length
{
    get
    {
        return _petal_length;
    }
    set
    {
        // validate range [0.5, 10]
        if (value >= 0.5 && value <= 10)
        {
            _petal_length = value;
        }
    }
}

private float _petal_width = 0.1f;
public float Petal_Width
{
    get
    {
        return _petal_width;
    }
    set
    {
        // validate range [0.1, 5]
        if (value >= 0.1 && value <= 5)
        {
            _petal_width = value;
        }
    }
}

Windows ML API 接受 ONNX 模型支持的四个描述性类的输入值:张量、序列、地图和图像。 在这种情况下,模型需要一个 32 位张量浮点对象,其形状为 float32[batch_size,4]。 由于批大小为 1,因此输入张量形状为 [1x4]。

若要创建张量输入,请使用 TensorFloat 类。

TensorFloat 类是命名空间的 Windows.AI.MachineLearning 一部分,用于定义 32 位浮点对象的张量 - 32 位浮点值的张量。 此类包含几个构建张量的有用方法。 在这种情况下,你将使用 CreateFromArray 方法以模型所需的确切大小生成张量输入。 我们将在评估方法中添加该调用。

绑定并评估模型

现在已定义模型输入张量并实例化训练的模型和会话,接下来可以创建一种方法来绑定和评估训练的机器学习模型。

此方法是机器学习应用的关键部分。 它包括对输入值的张量化处理和对模型输入的绑定。 稍后将在应用程序代码中使用此模型来评估模型。

若要绑定输入和输出,请使用 LearningModelBinding 该类。 机器学习模型具有输入和输出功能,用于将信息传入和传出模型。 请注意,Windows ML API 必须支持所需的功能。 LearningModelBinding 类被应用于 LearningModelSession 上,以将值绑定到命名的输入和输出特征。

LearningModelBinding 类具有多个预定义的方法,可用于将值绑定到这些命名特征。 在这里,你将使用 Bind 该方法将值绑定到模型。

若要评估您的模型并接收其结果,请调用相关的预定义评估方法,在本例中为 LearningModelSession 方法。 此方法将提供所需的功能,使用LearningModelBinding类提供的特征值来评估机器学习模型。

注释

要了解用于运行模型的其他评估方法,请通过查看 LearningModelSession 类文档,检查哪些方法可以在 LearningModelSession 上实现。

提取并显示结果

该模型以张量格式返回预测值作为 Tensor 浮点输出。 现在需要提取模型输出并显示正确的结果。 为此,你将通过运行 GetAsVectorView() 函数在预测输出上,将张量格式转换为向量。

该模型返回三个概率值,每个值表示一个特定的虹膜类型。 需要返回概率最高的标签。

  1. 将以下代码复制到 IrisModel.cs 该文件。
internal String Evaluate()
{
    // input tensor shape is [1x4]
    long[] shape = new long[2];
    shape[0] = 1;
    shape[1] = 4;

    // set up the input tensor
    float[] input_data = new float[4];
    input_data[0] = _sepal_length;
    input_data[1] = _sepal_width;
    input_data[2] = _petal_length;
    input_data[3] = _petal_width;
    TensorFloat tensor_float = TensorFloat.CreateFromArray(shape, input_data);

    // bind the tensor to "input"
    var binding = new LearningModelBinding(_session);
    binding.Bind("input", tensor_float);

    // evaluate
    var results = _session.Evaluate(binding, "");

    // get the results
    TensorFloat prediction = (TensorFloat)results.Outputs.First().Value;
    var prediction_data = prediction.GetAsVectorView();

    // find the highest predicted value
    int max_index = 0;
    float max_value = 0;
    for (int i = 0; i < prediction_data.Count; i++)
    {
        var val = prediction_data.ElementAt(i);
        if (val > max_value)
        {
            max_value = val;
            max_index = i;
        }
    }

    // return the label corresponding to the highest predicted value
    return _labels.ElementAt(max_index);
}

现已完成代码的机器学习部分。 现在,可以轻松地将模型与 Windows 应用程序集成。 在本教程的最后一部分,我们提供了一个基本的 Windows 图形用户界面和控制代码,以便使用你已创建的方法来测试模型。

创建应用程序 GUI

  1. 若要为应用创建 GUI 应用代码,请双击 MainPage.xaml 代码文件并打开 GUI 的预定义模板。

  2. 将以下代码复制粘贴到 MainPage.xaml,在 “Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" " Height="939"> 行的下方。

    <Grid Margin="30,30,30,30">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="title" HorizontalAlignment="Left" Text="Data Analysis App - Windows ML" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="32" TextDecorations="Underline" FontWeight="Bold"/>
        <TextBlock x:Name="subtitle" HorizontalAlignment="Left" Text="Provide the input :" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="20" Grid.Row="1" FontWeight="Bold"/>
        <Grid Grid.Row="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock x:Name="sepal_length" Text="sepal length in mm [range of 10 - 100]:" VerticalAlignment="Center"/>
            <TextBlock x:Name="sepal_width" Text="sepal width in mm [range of 10 - 80]:" VerticalAlignment="Center" Grid.Row="1"/>
            <TextBlock x:Name="petal_length" Text="petal length in mm [range of 5 - 100]:" VerticalAlignment="Center" Grid.Row="2"/>
            <TextBlock x:Name="petal_width" Text="sepal width in mm [range of 1 - 50]:" VerticalAlignment="Center" Grid.Row="3"/>

            <Slider x:Name="sepal_length_input" Minimum="10" Maximum="100" Orientation="Horizontal" Grid.Column="1" Width="200" ValueChanged="sepal_length_input_ValueChanged"/>
            <Slider x:Name="sepal_width_input" Minimum="10" Maximum="80" Orientation="Horizontal" Grid.Row="1" Grid.Column="1" Width="200" ValueChanged="sepal_width_input_ValueChanged"/>
            <Slider x:Name="petal_length_input" Minimum="5" Maximum="100" Orientation="Horizontal" Grid.Row="2" Grid.Column="1" Width="200" ValueChanged="petal_length_input_ValueChanged"/>
            <Slider x:Name="petal_width_input" Minimum="1" Maximum="50" Orientation="Horizontal" Grid.Row="3" Grid.Column="1" Width="200" ValueChanged="petal_width_input_ValueChanged"/>
        </Grid>
        <TextBlock x:Name="output" Text="Output:" FontSize="20" FontWeight="Bold" Grid.Row="3"/>
        <Grid Grid.Row="4">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock x:Name="output_subtitle" Text="Based on the information provided, the Iris type is:"/>
            <TextBlock x:Name="model_output" Text="Model output" FontStyle="Italic"  Grid.Column="1" Margin="10,0,0,0"/>
        </Grid>
    </Grid>

创建应用程序控件

应用程序控制代码 MainPage.xaml.cs包括运行应用的主要方法,以及运行模型和执行输出的几个步骤:

  1. 你将实例化之前在本教程中创建的类的新对象 IrisModel
  2. 你将调用在上一部分中生成的 Evaluate() 方法到模型上。 此方法将应用四次,其中一个用于每个输入参数:花瓣长度、花瓣宽度、花瓣长度和花瓣宽度。

应用将根据机器学习预测算法显示结果。

  1. 若要创建应用程序控制代码,请 MainPage.xaml.cs 双击代码文件并添加以下代码。
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace Iris_Data_Analysis
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private IrisModel _iris_model;

        public MainPage()
        {
            this.InitializeComponent();
            _iris_model = new IrisModel();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            _iris_model.Initialize();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
        }

        private void sepal_length_input_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            if (_iris_model != null)
            {
                _iris_model.Sepal_Length = (float)sepal_length_input.Value / 10.0f;
                model_output.Text = _iris_model.Evaluate();
            }
        }

        private void sepal_width_input_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            if (_iris_model != null)
            {
                _iris_model.Sepal_Width = (float)sepal_width_input.Value / 10.0f;
                model_output.Text = _iris_model.Evaluate();
            }
        }

        private void petal_length_input_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            if (_iris_model != null)
            {
                _iris_model.Petal_Length = (float)petal_length_input.Value / 10.0f;
                model_output.Text = _iris_model.Evaluate();
            }
        }

        private void petal_width_input_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            if (_iris_model != null)
            {
                _iris_model.Petal_Width = (float)petal_width_input.Value / 10.0f;
                model_output.Text = _iris_model.Evaluate();
            }
        }
    }
}

启动应用程序

现在,你已准备好启动应用程序并查看结果。

启用开发人员模式并从 Visual Studio 测试应用程序。 确保顶部工具栏中的下拉菜单设置为 Debug。 将解决方案平台更改为 x64(如果设备是 64 位的)或 x86(如果设备是 32 位的)以在你的本地计算机上运行该项目。

应用 GUI 包括四个滑块,用于更改所需参数的输入。 输入中的任何更改都将基于预测算法生成新的输出。 输出显示在输入滑块下方。

可以看到,给定的萼片长度 = 40mm,萼片宽度 = 50mm,花瓣长度 = 75mm,花瓣宽度 = 15mm,应用生成了杂色鸢尾类型的输入!

在应用中成功分类

概要

你刚刚创建了第一个 Windows 机器学习应用,从模型创建到成功执行。

其他资源

若要详细了解本教程中提到的主题,请访问以下资源: