教程:使用多类分类和 ML.NET 对支持问题进行分类

此示例教程展示如何使用 ML.NET 创建 GitHub 问题分类器来训练模型,该模型通过 Visual Studio 中的 C# 和 .NET 控制台应用程序用于分类和预测 GitHub 问题的 Area 标签。

本教程中,您将学习如何:

  • 准备数据
  • 转换数据
  • 训练模型
  • 评估模型
  • 使用训练的模型进行预测
  • 使用加载的模型部署和预测

可以在 dotnet/samples 存储库中找到本教程的源代码。

先决条件

创建控制台应用程序

创建项目

  1. 创建名为“GitHubIssueClassification”的 C# 控制台应用程序 。 选择“下一步”。

  2. 选择 .NET 7 作为要使用的框架。 选择 创建

  3. 在项目中创建名为 Data 的目录以保存数据集文件:

    解决方案资源管理器中,右键单击项目并选择“ 添加新>文件夹”。 键入“Data”,然后按 Enter

  4. 在项目中创建名为 Models 的目录以保存模型:

    解决方案资源管理器中,右键单击项目并选择“ 添加新>文件夹”。 键入“模型”,然后按 Enter

  5. 安装 Microsoft.ML NuGet 包

    注释

    此示例使用提到的 NuGet 包的最新稳定版本,除非另有说明。

    在解决方案资源管理器中,右键单击项目并选择“ 管理 NuGet 包”。 选择“nuget.org”作为包源,选择“浏览”选项卡,搜索 Microsoft.ML ,然后选择“ 安装”。 选择“预览更改”对话框中的“确定”按钮,然后选择“许可接受”对话框中的“我接受”按钮(如果同意列出的程序包的许可条款)。

准备数据

  1. 下载 issues_train.tsvissues_test.tsv 数据集,并将其保存到之前创建的 Data 文件夹中。 第一个数据集训练机器学习模型,第二个数据集可用于评估模型的准确性。

  2. 在解决方案资源管理器中,右键单击每个 *.tsv 文件并选择 “属性”。 在 高级 下,将 复制到输出目录 的值更改为 若较新则复制

创建类并定义路径

将以下附加 using 指令添加到 Program.cs 文件的顶部:

using Microsoft.ML;
using GitHubIssueClassification;

创建三个全局字段,用于保存最近下载的文件的路径以及 <a0/> 的全局变量,以及

  • _trainDataPath 具有用于训练模型的数据集的路径。
  • _testDataPath 具有用于评估模型的数据集的路径。
  • _modelPath 具有保存已训练模型的路径。
  • _mlContext是提供处理上下文的MLContext
  • _trainingDataView IDataView用于处理训练数据集。
  • _predEngine PredictionEngine<TSrc,TDst>用于单个预测。

将以下代码添加到指令正下方的 using 行,以指定这些路径和其他变量:

string _appPath = Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]) ?? ".";
string _trainDataPath = Path.Combine(_appPath, "..", "..", "..", "Data", "issues_train.tsv");
string _testDataPath = Path.Combine(_appPath, "..", "..", "..", "Data", "issues_test.tsv");
string _modelPath = Path.Combine(_appPath, "..", "..", "..", "Models", "model.zip");

MLContext _mlContext;
PredictionEngine<GitHubIssue, IssuePrediction> _predEngine;
ITransformer _trainedModel;
IDataView _trainingDataView;

为输入数据和预测创建一些类。 向项目添加新类:

  1. 解决方案资源管理器中,右键单击该项目,然后选择“ 添加新>”。

  2. 在“ 添加新项 ”对话框中,选择“ ”并将“ 名称 ”字段更改为 GitHubIssueData.cs。 然后选择“添加”。

    GitHubIssueData.cs文件将在代码编辑器中打开。 将以下 using 指令添加到 GitHubIssueData.cs顶部:

    using Microsoft.ML.Data;
    
  3. 删除现有类定义,并将以下代码添加到 GitHubIssueData.cs 文件。 此代码有两个类, GitHubIssue 以及 IssuePrediction

    public class GitHubIssue
    {
        [LoadColumn(0)]
        public string? ID { get; set; }
        [LoadColumn(1)]
        public string? Area { get; set; }
        [LoadColumn(2)]
        public required string Title { get; set; }
        [LoadColumn(3)]
        public required string Description { get; set; }
    }
    
    public class IssuePrediction
    {
        [ColumnName("PredictedLabel")]
        public string? Area;
    }
    

    是要 label 预测的列。 标识的 Features 是为模型提供用于预测标签的输入。

    使用 LoadColumnAttribute 指定数据集中源列的索引。

    GitHubIssue 是输入数据集类,具有以下 String 字段:

    • 第一列 ID (GitHub 问题 ID)。
    • 第二列 Area (用于训练的预测值)。
    • 第三列(GitHub 问题标题)是用于预测第Title一列。featureArea
    • 第四列是用于预测第二DescriptionfeatureArea

    IssuePrediction 是训练模型后用于预测的类。 它有一个stringArea)和一个PredictedLabelColumnName属性。 在 PredictedLabel 预测和评估期间使用。 对于计算,使用包含训练数据的输入、预测值和模型。

    所有 ML.NET 操作都从 MLContext 类开始。 初始化 mlContext 可以创建一个可在模型创建工作流对象之间共享的新 ML.NET 环境。 在概念上,DBContext 类似于 Entity Framework

初始化变量

使用 _mlContext 随机种子的新实例 MLContext ()初始化全局变量,seed: 0以便在多个训练中重复/确定性结果。 将 Console.WriteLine("Hello World!") 行替换为以下代码:

_mlContext = new MLContext(seed: 0);

加载数据

ML.NET 使用 IDataView 接口 作为描述数字或文本表格数据的灵活高效方法。 IDataView 可以加载文本文件或实时(例如 SQL 数据库或日志文件)。

若要初始化和加载 _trainingDataView 全局变量以便将其用于管道,请在初始化后 mlContext 添加以下代码:

_trainingDataView = _mlContext.Data.LoadFromTextFile<GitHubIssue>(_trainDataPath,hasHeader: true);

LoadFromTextFile() 定义数据架构并在文件中读取。 它采用数据路径变量并返回一个 IDataView

调用 LoadFromTextFile() 该方法后添加以下内容:

var pipeline = ProcessData();

该方法 ProcessData 执行以下任务:

  • 提取和转换数据。
  • 返回处理管道。

ProcessData使用以下代码在Program.cs文件的底部创建方法:

IEstimator<ITransformer> ProcessData()
{

}

提取特征并转换数据

若要预测领域 GitHub 标签,使用 MapValueToKey() 方法将 Area 列转换为数值键类型 Label 列(分类算法接受的格式),并将其添加为新的数据集列:

var pipeline = _mlContext.Transforms.Conversion.MapValueToKey(inputColumnName: "Area", outputColumnName: "Label")

接下来,调用 mlContext.Transforms.Text.FeaturizeText,它将文本(TitleDescription)列转换为每个被调用 TitleFeaturizedDescriptionFeaturized的数值向量。 使用以下代码将这两列的特征化追加到管道:

.Append(_mlContext.Transforms.Text.FeaturizeText(inputColumnName: "Title", outputColumnName: "TitleFeaturized"))
.Append(_mlContext.Transforms.Text.FeaturizeText(inputColumnName: "Description", outputColumnName: "DescriptionFeaturized"))

数据准备的最后一步使用 Concatenate() 方法将所有特征列合并到功能列。 默认情况下,学习算法仅处理 “功能 ”列中的功能。 使用以下代码将此转换追加到管道:

.Append(_mlContext.Transforms.Concatenate("Features", "TitleFeaturized", "DescriptionFeaturized"))

接下来,追加一个用于缓存 DataView 的缓存 AppendCacheCheckpoint ,因此,使用缓存多次循环访问数据时,性能可能会更好,如以下代码所示:

.AppendCacheCheckpoint(_mlContext);

警告

将 AppendCacheCheckpoint 用于小型/中等数据集以降低训练时间。 请勿使用它(删除 。处理非常大的数据集时,AppendCacheCheckpoint()。

返回方法末尾的 ProcessData 管道。

return pipeline;

此步骤处理预处理/特征化。 使用 ML.NET 中提供的其他组件可以让你的模型获得更好的结果。

生成和训练模型

在调用方法后,将以下调用 BuildAndTrainModel添加为方法的 ProcessData() 下一行:

var trainingPipeline = BuildAndTrainModel(_trainingDataView, pipeline);

该方法 BuildAndTrainModel 执行以下任务:

  • 创建训练算法类。
  • 训练模型。
  • 根据训练数据预测区域。
  • 返回模型。

BuildAndTrainModel使用以下代码在方法声明ProcessData()之后创建方法:

IEstimator<ITransformer> BuildAndTrainModel(IDataView trainingDataView, IEstimator<ITransformer> pipeline)
{

}

关于分类任务

分类是一项机器学习任务,它使用数据来确定项目或数据行的类别、类型或类,并且通常是以下类型之一:

  • 二进制:A 或 B。
  • 多类:可以使用单个模型预测的多个类别。

对于此类问题,请使用多类分类学习算法,因为问题类别预测可以是多个类别(多类)之一,而不仅仅是两个(二进制)。

通过将以下代码添加为以下代码行 BuildAndTrainModel(),将机器学习算法追加到数据转换定义:

var trainingPipeline = pipeline.Append(_mlContext.MulticlassClassification.Trainers.SdcaMaximumEntropy("Label", "Features"))
        .Append(_mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

SdcaMaximumEntropy 是多类分类训练算法。 这会追加到 pipeline 特征化 TitleDescriptionFeatures) 和 Label 输入参数中,以便从历史数据中学习。

训练模型

通过将以下内容添加为方法中的splitTrainSet下一行代码,使模型适应BuildAndTrainModel()数据并返回训练的模型:

_trainedModel = trainingPipeline.Fit(trainingDataView);

该方法 Fit()通过转换数据集并应用训练来训练模型。

PredictionEngine 是一种方便的 API,可用于传入并针对单个数据实例执行预测。 将此添加为方法中的 BuildAndTrainModel() 下一行:

_predEngine = _mlContext.Model.CreatePredictionEngine<GitHubIssue, IssuePrediction>(_trainedModel);

使用训练的模型进行预测

通过创建以下实例Predict,添加 GitHub 问题以测试方法中GitHubIssue训练的模型的预测:

GitHubIssue issue = new GitHubIssue() {
    Title = "WebSockets communication is slow in my machine",
    Description = "The WebSockets communication used under the covers by SignalR looks like is going slow in my development machine.."
};

使用 Predict() 函数对单个数据行进行预测:

var prediction = _predEngine.Predict(issue);

使用模型:预测结果

显示 GitHubIssue 和相应的 Area 标签预测,以便共享结果并相应地对其执行作。 使用以下 Console.WriteLine() 代码为结果生成显示:

Console.WriteLine($"=============== Single Prediction just-trained-model - Result: {prediction.Area} ===============");

返回训练以用于评估的模型

返回方法末尾的 BuildAndTrainModel 模型。

return trainingPipeline;

评估模型

创建并训练模型后,需要使用不同的数据集对其进行评估,以便进行质量保证和验证。 在 Evaluate 方法中,将传入创建的 BuildAndTrainModel 模型以进行评估。 按照 Evaluate 以下代码所示创建方法 BuildAndTrainModel

void Evaluate(DataViewSchema trainingDataViewSchema)
{

}

该方法 Evaluate 执行以下任务:

  • 加载测试数据集。
  • 创建多类评估器。
  • 评估模型并创建指标。
  • 显示指标。

使用以下代码在 BuildAndTrainModel 方法调用的下面添加对新方法的调用:

Evaluate(_trainingDataView.Schema);

与之前使用训练数据集一样,通过将以下代码添加到方法来 Evaluate 加载测试数据集:

var testDataView = _mlContext.Data.LoadFromTextFile<GitHubIssue>(_testDataPath,hasHeader: true);

Evaluate() 方法使用指定的数据集计算模型的质量指标。 它返回一个 MulticlassClassificationMetrics 对象,该对象包含多类分类计算器计算的总体指标。 若要显示指标以确定模型的质量,需要先获取它们。 请注意,使用机器学习全局变量(_trainedModel)的 Transform()方法输入特征并返回预测。 将以下代码作为下一行添加到 Evaluate 方法:

var testMetrics = _mlContext.MulticlassClassification.Evaluate(_trainedModel.Transform(testDataView));

以下指标针对多类分类进行评估:

  • Micro Accuracy - 每个样本类对都同样有助于准确性指标。 希望 Micro Accuracy 尽可能接近一个。
  • 宏准确性 - 每个类都同样有助于准确性指标。 少数类的权重与较大的类相同。 希望宏准确性尽可能接近一个。
  • 日志丢失 - 请参阅 日志丢失。 希望日志丢失尽可能接近零。
  • 对数损失减少 - 范围为 [-inf, 1.00],其中 1.00 是完美的预测,0 表示平均预测。 希望减少日志损失尽可能接近一个。

显示模型验证的指标

使用以下代码显示指标、共享结果,然后对其执行作:

Console.WriteLine($"*************************************************************************************************************");
Console.WriteLine($"*       Metrics for Multi-class Classification model - Test Data     ");
Console.WriteLine($"*------------------------------------------------------------------------------------------------------------");
Console.WriteLine($"*       MicroAccuracy:    {testMetrics.MicroAccuracy:0.###}");
Console.WriteLine($"*       MacroAccuracy:    {testMetrics.MacroAccuracy:0.###}");
Console.WriteLine($"*       LogLoss:          {testMetrics.LogLoss:#.###}");
Console.WriteLine($"*       LogLossReduction: {testMetrics.LogLossReduction:#.###}");
Console.WriteLine($"*************************************************************************************************************");

将模型保存到文件

对模型感到满意后,请将其保存到文件中,以便稍后或在另一个应用程序中进行预测。 将以下代码添加到 Evaluate 方法中。

SaveModelAsFile(_mlContext, trainingDataViewSchema, _trainedModel);

SaveModelAsFile 方法下方 Evaluate 创建方法。

void SaveModelAsFile(MLContext mlContext,DataViewSchema trainingDataViewSchema, ITransformer model)
{

}

将以下代码添加到 SaveModelAsFile 方法。 此代码使用 Save 该方法将训练的模型序列化并存储为 zip 文件。

mlContext.Model.Save(model, trainingDataViewSchema, _modelPath);

使用模型部署和预测

使用以下代码将调用添加到新方法的右侧 Evaluate

PredictIssue();

使用以下PredictIssue代码在方法(方法前面Evaluate)创建SaveModelAsFile方法:

void PredictIssue()
{

}

该方法 PredictIssue 执行以下任务:

  • 加载保存的模型。
  • 创建单个测试数据问题。
  • 根据测试数据预测区域。
  • 合并用于报告的测试数据和预测。
  • 显示预测的结果。

通过将以下代码添加到 PredictIssue 方法,将保存的模型加载到应用程序中:

ITransformer loadedModel = _mlContext.Model.Load(_modelPath, out var modelInputSchema);

通过创建以下实例Predict,添加 GitHub 问题以测试方法中GitHubIssue训练的模型的预测:

GitHubIssue singleIssue = new GitHubIssue() { Title = "Entity Framework crashes", Description = "When connecting to the database, EF is crashing" };

如前所述,使用以下代码创建实例 PredictionEngine

_predEngine = _mlContext.Model.CreatePredictionEngine<GitHubIssue, IssuePrediction>(loadedModel);

PredictionEngine 是一种方便的 API,可用于对单个数据实例执行预测。 PredictionEngine 不是线程安全的。 可以在单线程或原型环境中使用。 为了提高生产环境中的性能和线程安全性,请使用PredictionEnginePool该服务,该服务会创建一个ObjectPoolPredictionEngine对象用于整个应用程序。 请参阅本指南,了解如何在 ASP.NET Core Web API 中使用PredictionEnginePool

注释

PredictionEnginePool 服务扩展目前为预览版。

PredictionEngine通过向预测方法添加以下代码PredictIssue来预测区域 GitHub 标签:

var prediction = _predEngine.Predict(singleIssue);

使用加载的模型进行预测

显示 Area 以便对问题进行分类并相应地采取行动。 使用以下 Console.WriteLine() 代码为结果生成显示:

Console.WriteLine($"=============== Single Prediction - Result: {prediction.Area} ===============");

Results

结果应如下所示。 当管道处理时,它会显示消息。 你可能会看到警告或处理消息。 为了清楚起见,这些消息已从以下结果中删除。

=============== Single Prediction just-trained-model - Result: area-System.Net ===============
*************************************************************************************************************
*       Metrics for Multi-class Classification model - Test Data
*------------------------------------------------------------------------------------------------------------
*       MicroAccuracy:    0.738
*       MacroAccuracy:    0.668
*       LogLoss:          .919
*       LogLossReduction: .643
*************************************************************************************************************
=============== Single Prediction - Result: area-System.Data ===============

祝贺! 现已成功生成机器学习模型,用于对 GitHub 问题的区域标签进行分类和预测。 可以在 dotnet/samples 存储库中找到本教程的源代码。

后续步骤

在本教程中,你将学习到如何:

  • 准备数据
  • 转换数据
  • 训练模型
  • 评估模型
  • 使用训练的模型进行预测
  • 使用加载的模型部署和预测

请继续学习下一篇教程,了解详细信息。