教程:将多类分类与 ML.NET 配合使用,对支持问题分类
本示例教程演示如何使用 ML.NET 创建 GitHub 问题分类器来训练模型,使其通过 Visual Studio 中使用 C# 的 .NET Core 控制台应用程序为 GitHub 问题分类和预测区域标签。
在本教程中,你将了解:
- 准备数据
- 转换数据
- 定型模型
- 评估模型
- 使用训练的模型预测
- 使用加载模型部署和预测
可以在 dotnet/samples 存储库中找到本教程的源代码。
先决条件
- 已安装“.NET 桌面开发”工作负载的 Visual Studio 2022。
- GitHub 问题制表符分隔文件 (issues_train.tsv)。
- GitHub 问题测试制表符分隔文件 (issues_test.tsv)。
创建控制台应用程序
创建项目
创建名为“GitHubIssueClassification”的 C# 控制台应用程序。 选择“下一步” 。
选择 .NET 7 作为要使用的框架。 选择“创建”。
在项目中创建一个名为“Data”的目录来保存数据集文件:
在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” >“新文件夹” 。 键入“Data”,然后按 Enter。
在项目中创建一个名为“Models”的目录来保存模型:
在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” >“新文件夹” 。 键入“Models”,然后按 Enter。
安装“Microsoft.ML NuGet 包” :
注意
除非另有说明,否则本示例使用前面提到的 NuGet 包的最新稳定版本。
在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。 选择“nuget.org”作为包源,然后选择“浏览”选项卡并搜索“Microsoft.ML”,再选择“安装”按钮 。 选择“预览更改” 对话框上的“确定” 按钮,如果你同意所列包的许可条款,则选择“接受许可” 对话框上的“我接受” 按钮。
准备数据
下载 issues_train.tsv 和 issues_test.tsv 数据集,并将它们保存到先前创建的“Data”文件夹。 第一个数据集用于定型机器学习模型,第二个数据集可用来评估模型的准确度。
在“解决方案资源管理器”中,右键单击每个 *.tsv 文件,然后选择“属性”。 在“高级”下,将“复制到输出目录”的值更改为“如果较新则复制” 。
创建类和定义路径
将以下附加的 using
语句添加到“Program.cs”文件顶部:
using Microsoft.ML;
using GitHubIssueClassification;
创建 3 个全局字段,来保存最近下载的文件的路径以及 MLContext
、DataView
和 PredictionEngine
的全局变量:
_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;
为输入数据和预测创建一些类。 向项目添加一个新类:
在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” >“新项” 。
在“添加新项”对话框中,选择“类”并将“名称”字段更改为“GitHubIssueData.cs” 。 然后,选择“添加”按钮。
“GitHubIssueData.cs”文件随即在代码编辑器中打开。 将下面的
using
语句添加到 GitHubIssueData.cs 的顶部:
using Microsoft.ML.Data;
删除现有类定义并向“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
(定型预测) - 第三列
Title
(GitHub 问题标题)是用于预测Area
的第一个feature
- 第四列
Description
是用于预测Area
的第二个feature
IssuePrediction
是在定型模型后用于预测的类。 它有一个 string
(Area
) 和一个 PredictedLabel
ColumnName
属性。 PredictedLabel
在预测和评估过程中使用。 对于计算,将使用带定型数据的输入、预测值和模型。
所有 ML.NET 操作都从 MLContext 类开始。 初始化 mlContext
创建了新的 ML.NET 环境,可以在模型创建工作流对象之间共享。 从概念上讲,它与 Entity Framework
中的 DBContext
类似。
初始化变量
使用具有随机种子 (seed: 0
) 的新实例 MLContext
初始化 _mlContext
全局变量,以获得跨多个定型的可重复/确定性结果。 使用以下代码替换 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
方法执行以下任务:
- 提取并转换数据。
- 返回处理管道。
使用以下代码在 Program.cs 文件底部创建 ProcessData
方法:
IEstimator<ITransformer> ProcessData()
{
}
提取功能和转换数据
由于要预测 GitHubIssue
的区域 GitHub 标签,因此请使用 MapValueToKey() 方法将 Area
列转换为数字键类型 Label
列(分类算法所接受的格式)并将其添加为新的数据集列:
var pipeline = _mlContext.Transforms.Conversion.MapValueToKey(inputColumnName: "Area", outputColumnName: "Label")
接下来,调用 mlContext.Transforms.Text.FeaturizeText
,它会将文本(Title
和 Description
)列转换为每个名为 TitleFeaturized
和 DescriptionFeaturized
的值的数字向量。 使用以下代码将两列的特征化附加到管道:
.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"))
接下来,附加一个 AppendCacheCheckpoint 来缓存数据视图,以便在使用缓存多次循环访问数据时获得更好的性能,如下面的代码所示:
.AppendCacheCheckpoint(_mlContext);
警告
对小/中型数据集使用 AppendCacheCheckpoint 可以降低训练时间。 在处理大型数据集时不使用它(删除 .AppendCacheCheckpoint())。
在 ProcessData
方法的末尾返回管道。
return pipeline;
此步骤处理预处理/特征化。 使用 ML.NET 中可用的其他组件可以在使用模型时生成更佳结果。
生成和定型模型
将以下 BuildAndTrainModel
方法调用添加为 ProcessData()
方法调用后面的下一行:
var trainingPipeline = BuildAndTrainModel(_trainingDataView, pipeline);
BuildAndTrainModel
方法执行以下任务:
- 创建定型算法类。
- 定型模型。
- 根据定型数据预测区域。
- 返回模型。
使用以下代码在 ProcessData()
方法的声明后面创建 BuildAndTrainModel
方法:
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
并接受特征化的 Title
和 Description
(Features
) 以及 Label
输入参数,以便从历史数据中学习。
定型模型
在 BuildAndTrainModel()
方法中添加以下代码作为下一代码行,使模型适应 splitTrainSet
数据,并返回经过训练的模型:
_trainedModel = trainingPipeline.Fit(trainingDataView);
Fit()
方法通过转换数据集并应用训练来训练模型。
PredictionEngine 是一个简便 API,可用于传入单个数据实例,然后对其执行预测。 将此 API 添加为 BuildAndTrainModel()
方法中的下一行:
_predEngine = _mlContext.Model.CreatePredictionEngine<GitHubIssue, IssuePrediction>(_trainedModel);
使用训练的模型预测
通过创建一个 GitHubIssue
实例,在 Predict
方法中添加一个 GitHub 问题来测试定型模型的预测:
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
中创建的模型以进行评估。 紧随 BuildAndTrainModel
后创建 Evaluate
方法,如以下代码所示:
void Evaluate(DataViewSchema trainingDataViewSchema)
{
}
Evaluate
方法执行以下任务:
- 加载测试数据集。
- 创建多类评估程序。
- 评估模型并创建指标。
- 显示指标。
使用以下代码在 BuildAndTrainModel
方法调用的下方添加对新方法的调用:
Evaluate(_trainingDataView.Schema);
与之前对训练数据集所执行的操作那样,通过将以下代码添加到 Evaluate
方法来加载测试数据集:
var testDataView = _mlContext.Data.LoadFromTextFile<GitHubIssue>(_testDataPath,hasHeader: true);
Evaluate() 方法使用指定数据集计算模型的质量指标。 它返回 MulticlassClassificationMetrics 对象,其中包含由多类分类计算器计算出的总体指标。
要显示指标来确定模型质量,需要先获取这些指标。
请注意使用机器学习 _trainedModel
全局变量 (ITransformer) 的 Transform() 方法来输入特征和返回预测。 将以下代码作为下一行添加到 Evaluate
方法中:
var testMetrics = _mlContext.MulticlassClassification.Evaluate(_trainedModel.Transform(testDataView));
针对多类分类评估以下指标:
微观准确性 - 每个“样本-类”对准确性指标的贡献度相同。 通常会希望微观准确性尽可能接近 1。
宏观准确性 - 每个类对准确性指标的贡献度相同。 占比较小的类与占比较大的类拥有同等的权重。 通常会希望宏观准确性尽可能接近 1。
对数损失 - 请参阅对数损失。 通常会希望对数损失尽可能接近 0。
对数损失减小 - 取值范围为 [-inf,1.00],其中 1.00 表示非常精准的预测结果,0 表示准确性一般的预测。 通常会希望对数损失减少尽可能接近 1。
显示用于模型验证的指标
使用下面的代码显示指标、共享结果,然后处理它们:
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);
在 Evaluate
方法下创建 SaveModelAsFile
方法。
void SaveModelAsFile(MLContext mlContext,DataViewSchema trainingDataViewSchema, ITransformer model)
{
}
将以下代码添加到 SaveModelAsFile
方法。 此代码使用 Save
方法对训练后的模型进行序列化并将其存储为 zip 文件。
mlContext.Model.Save(model, trainingDataViewSchema, _modelPath);
使用模型进行部署和预测
使用以下代码在 Evaluate
方法调用的下方添加对新方法的调用:
PredictIssue();
使用以下代码恰好在 Evaluate
方法的后面(恰在 SaveModelAsFile
方法之前)创建 PredictIssue
方法:
void PredictIssue()
{
}
PredictIssue
方法执行以下任务:
- 加载已保存的模型
- 创建测试数据的单个问题。
- 根据测试数据预测区域。
- 结合测试数据和预测进行报告。
- 显示预测结果。
通过向 PredictIssue
方法中添加以下代码,将保存的模型加载到应用程序中:
ITransformer loadedModel = _mlContext.Model.Load(_modelPath, out var modelInputSchema);
通过创建一个 GitHubIssue
实例,在 Predict
方法中添加一个 GitHub 问题来测试定型模型的预测:
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
服务,这将创建一个在整个应用程序中使用的 PredictionEngine
对象的 ObjectPool
。 请参阅本指南,了解如何在 ASP.NET Core Web API 中使用 PredictionEnginePool
。
注意
PredictionEnginePool
服务扩展目前处于预览状态。
通过将以下代码添加到预测的 PredictIssue
方法,使用 PredictionEngine
来预测区域 GitHub 标签:
var prediction = _predEngine.Predict(singleIssue);
使用加载后的模型进行预测
显示 Area
以便对问题进行分类并对其进行相应操作。 使用以下 Console.WriteLine() 代码创建结果显示:
Console.WriteLine($"=============== Single Prediction - Result: {prediction.Area} ===============");
结果
结果应如下所示。 管道处理期间,会显示消息。 你可能会看到警告或处理消息。 为简便起见,已从以下结果中删除这些消息。
=============== 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 存储库中找到本教程的源代码。
后续步骤
在本教程中,你将了解:
- 准备数据
- 转换数据
- 定型模型
- 评估模型
- 使用训练的模型预测
- 使用加载模型部署和预测
进入下一教程了解详细信息