教程:在 ML.NET 中使用二元分类分析网站注释的情绪

本教程介绍如何创建一个 .NET 控制台应用程序,该应用程序将情绪从网站注释分类并采取相应的作。 二进制情绪分类器在 Visual Studio 2022 中使用 C# 。

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

  • 创建控制台应用程序
  • 准备数据
  • 加载数据
  • 生成和训练模型
  • 评估模型
  • 使用模型进行预测
  • 查看结果

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

先决条件

创建控制台应用程序

  1. 创建名为“SentimentAnalysis”的 C# 控制台应用程序 。 单击“下一步”按钮。

  2. 选择 .NET 8 作为要使用的框架。 单击“创建” 按钮。

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

  4. 安装 Microsoft.ML NuGet 包

    注释

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

    在解决方案资源管理器中,右键单击项目并选择“ 管理 NuGet 包”。 选择“nuget.org”作为包源,然后选择“ 浏览 ”选项卡。搜索 Microsoft.ML,选择所需的包,然后选择“ 安装”。 通过同意所选程序包的许可条款继续安装。

准备数据

注释

本教程的数据集来自《从群体标签到个体标签:使用深度特征》,由 Kotzias 等人提供。 KDD 2015,并托管在 UCI 机器学习存储库 - Dua、D. 和 Karra Taniskidou,E. (2017 年)。 UCI 机器学习存储库 [http://archive.ics.uci.edu/ml]。 加州大学欧文分校的信息与计算机科学学院。

  1. 下载 UCI 情绪标记句子数据集 ZIP 文件,然后解压缩。

  2. yelp_labelled.txt 文件复制到所创建 的数据 目录中。

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

创建类并定义路径

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

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using SentimentAnalysis;
    using static Microsoft.ML.DataOperationsCatalog;
    
  2. 将以下代码添加到指令正下方的 using 行,以创建一个字段来保存最近下载的数据集文件路径:

    string _dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "yelp_labelled.txt");
    
  3. 接下来,为输入数据和预测创建类。 向项目添加新类:

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

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

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

    using Microsoft.ML.Data;
    
  5. 删除现有类定义,并将以下代码(具有两个类 SentimentDataSentimentPrediction添加到 SentimentData.cs 文件中:

    public class SentimentData
    {
        [LoadColumn(0)]
        public string? SentimentText;
    
        [LoadColumn(1), ColumnName("Label")]
        public bool Sentiment;
    }
    
    public class SentimentPrediction : SentimentData
    {
    
        [ColumnName("PredictedLabel")]
        public bool Prediction { get; set; }
    
        public float Probability { get; set; }
    
        public float Score { get; set; }
    }
    

数据准备方式

输入数据集类 SentimentData 具有一个 string,用于用户评论(SentimentText),以及一个表示情感的 bool,其值为Sentiment 1(正面)或 0(负面)。 这两个字段都附加了 LoadColumn 属性,用于描述每个字段的数据文件顺序。 此外,该 Sentiment 属性还有一个 ColumnName 属性,用于将其指定为 Label 字段。 以下示例文件没有标题行,如下所示:

SentimentText 情绪(标签)
服务员服务速度有点慢。 0
地壳不好。 0
哇。。。喜欢这个地方。 1
服务非常提示。 1

SentimentPrediction 是模型训练后使用的预测类。 它继承自 SentimentData 以便可以显示输入 SentimentText 以及输出预测。 模型在提供新输入SentimentText时预测的值是布尔值Prediction

输出类 SentimentPrediction 包含模型计算的另外两个属性: Score -模型计算的原始分数,以及 Probability 将分数校准为文本具有积极情绪的可能性。

对于本教程,最重要的属性是 Prediction

加载数据

ML.NET 中的数据表示为 IDataView 接口IDataView 是描述表格数据(数字和文本)的灵活高效方法。 可以将数据从文本文件或实时(例如 SQL 数据库或日志文件)加载到对象 IDataView

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

准备应用,然后加载数据:

  1. Console.WriteLine("Hello World!") 行替换为以下代码来声明和初始化 mlContext 变量:

    MLContext mlContext = new MLContext();
    
  2. 将以下内容添加为下一行代码:

    TrainTestData splitDataView = LoadData(mlContext);
    
  3. 使用以下代码在Program.cs文件的底部创建一个LoadData()方法:

    TrainTestData LoadData(MLContext mlContext)
    {
    
    }
    

    该方法 LoadData() 执行以下任务:

    • 加载数据。
    • 将加载的数据集拆分为训练数据集和测试数据集。
    • 返回拆分训练和测试数据集。
  4. 将以下代码添加为方法的第 LoadData() 一行:

    IDataView dataView = mlContext.Data.LoadFromTextFile<SentimentData>(_dataPath, hasHeader: false);
    

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

拆分用于模型训练和测试的数据集

准备模型时,可以使用数据集的一部分来训练模型,并使用数据集的一部分来测试模型的准确性。

  1. 若要将加载的数据拆分为所需的数据集,请在方法中添加 LoadData() 以下代码作为下一行:

    TrainTestData splitDataView = mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2);
    

    前面的代码使用 TrainTestSplit() 方法将加载的数据集拆分为训练和测试数据集,并在类中 DataOperationsCatalog.TrainTestData 返回它们。 使用 testFraction参数指定数据的测试集百分比。 默认值为 10%,在本例中,使用 20% 来评估更多数据。

  2. splitDataView返回方法末尾LoadData()

    return splitDataView;
    

生成和训练模型

  1. 将以下调用添加到 BuildAndTrainModel方法下面的方法调用 LoadData

    ITransformer model = BuildAndTrainModel(mlContext, splitDataView.TrainSet);
    

    该方法 BuildAndTrainModel() 执行以下任务:

    • 提取和转换数据。
    • 训练模型。
    • 根据测试数据预测情绪。
    • 返回模型。
  2. BuildAndTrainModel()使用以下代码在方法下方LoadData()创建方法:

    ITransformer BuildAndTrainModel(MLContext mlContext, IDataView splitTrainSet)
    {
    
    }
    

提取和转换数据

  1. 调用 FeaturizeText 为下一行代码:

    var estimator = mlContext.Transforms.Text.FeaturizeText(outputColumnName: "Features", inputColumnName: nameof(SentimentData.SentimentText))
    

    FeaturizeText()上一代码中的方法将文本列 (SentimentText) 转换为机器学习算法使用的数值键类型Features列,并将其添加为新的数据集列:

    SentimentText 情绪 Features
    服务员服务速度有点慢。 0 [0.76, 0.65, 0.44, …]
    地壳不好。 0 [0.98, 0.43, 0.54, …]
    哇。。。喜欢这个地方。 1 [0.35, 0.73, 0.46, …]
    服务非常提示。 1 [0.39, 0, 0.75, …]

添加学习算法

此应用使用分类算法对项目或数据行进行分类。 应用将网站评论分类为正数或负数,因此请使用二元分类任务。

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

.Append(mlContext.BinaryClassification.Trainers.SdcaLogisticRegression(labelColumnName: "Label", featureColumnName: "Features"));

SdcaLogisticRegressionBinaryTrainer 是分类训练算法。 这会附加到 estimator 特征化 SentimentTextFeatures)和 Label 输入参数,以便从历史数据中学习。

训练模型

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

Console.WriteLine("=============== Create and Train the Model ===============");
var model = estimator.Fit(splitTrainSet);
Console.WriteLine("=============== End of training ===============");
Console.WriteLine();

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

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

返回方法末尾的 BuildAndTrainModel() 模型:

return model;

评估模型

训练模型后,使用测试数据来验证模型的性能。

  1. 使用以下 Evaluate() 代码创建方法 BuildAndTrainModel()

    void Evaluate(MLContext mlContext, ITransformer model, IDataView splitTestSet)
    {
    
    }
    

    该方法 Evaluate() 执行以下任务:

    • 加载测试数据集。
    • 创建 BinaryClassification 计算器。
    • 评估模型并创建指标。
    • 显示指标。
  2. 使用以下代码在方法调用下方 BuildAndTrainModel 添加对新方法的调用:

    Evaluate(mlContext, model, splitDataView.TestSet);
    
  3. 通过将以下代码添加到以下代码splitTestSetEvaluate()转换数据:

    Console.WriteLine("=============== Evaluating Model accuracy with Test data===============");
    IDataView predictions = model.Transform(splitTestSet);
    

    前面的代码使用 Transform() 方法对测试数据集的多个提供的输入行进行预测。

  4. 通过将以下内容添加为方法中的 Evaluate() 下一行代码来评估模型:

    CalibratedBinaryClassificationMetrics metrics = mlContext.BinaryClassification.Evaluate(predictions, "Label");
    

获得预测集(predictions)后, Evaluate() 方法将评估模型,该模型将预测值与测试数据集中的实际 Labels 值进行比较,并返回 一个BinaryClassificationMetrics 对象,说明模型的性能。

显示模型验证的指标

使用以下代码显示指标:

Console.WriteLine();
Console.WriteLine("Model quality metrics evaluation");
Console.WriteLine("--------------------------------");
Console.WriteLine($"Accuracy: {metrics.Accuracy:P2}");
Console.WriteLine($"Auc: {metrics.AreaUnderRocCurve:P2}");
Console.WriteLine($"F1Score: {metrics.F1Score:P2}");
Console.WriteLine("=============== End of model evaluation ===============");
  • Accuracy 指标获取模型的准确性,该模型是测试集中正确预测的比例。

  • AreaUnderRocCurve 指标指示模型如何正确地对正类和负类进行分类。 希望 AreaUnderRocCurve 尽可能接近一个。

  • F1Score 指标获取模型的 F1 分数,这是 精度召回率之间的平衡度量值。 希望 F1Score 尽可能接近一个。

预测测试数据结果

  1. UseModelWithSingleItem()使用以下代码在方法后面Evaluate()创建方法:

    void UseModelWithSingleItem(MLContext mlContext, ITransformer model)
    {
    
    }
    

    该方法 UseModelWithSingleItem() 执行以下任务:

    • 创建测试数据的单个注释。
    • 根据测试数据预测情绪。
    • 合并用于报告的测试数据和预测。
    • 显示预测的结果。
  2. Evaluate()方法调用的正下方,使用以下代码为新方法添加调用:

    UseModelWithSingleItem(mlContext, model);
    
  3. 添加以下代码以在方法中创建 UseModelWithSingleItem() 为第一行:

    PredictionEngine<SentimentData, SentimentPrediction> predictionFunction = mlContext.Model.CreatePredictionEngine<SentimentData, SentimentPrediction>(model);
    

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

    注释

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

  4. 通过创建以下UseModelWithSingleItem()实例,添加注释以测试方法中SentimentData训练的模型的预测:

    SentimentData sampleStatement = new SentimentData
    {
        SentimentText = "This was a very bad steak"
    };
    
  5. 通过在UseModelWithSingleItem()方法中添加以下代码作为下一行代码,将测试注释数据传递给PredictionEngine

    var resultPrediction = predictionFunction.Predict(sampleStatement);
    

    Predict() 函数对单个数据行进行预测。

  6. 使用以下代码显示 SentimentText 和相应的情绪预测:

    Console.WriteLine();
    Console.WriteLine("=============== Prediction Test of model with a single sample and test dataset ===============");
    
    Console.WriteLine();
    Console.WriteLine($"Sentiment: {resultPrediction.SentimentText} | Prediction: {(Convert.ToBoolean(resultPrediction.Prediction) ? "Positive" : "Negative")} | Probability: {resultPrediction.Probability} ");
    
    Console.WriteLine("=============== End of Predictions ===============");
    Console.WriteLine();
    

使用模型进行预测

部署和预测批处理项

  1. UseModelWithBatchItems()使用以下代码在方法后面UseModelWithSingleItem()创建方法:

    void UseModelWithBatchItems(MLContext mlContext, ITransformer model)
    {
    
    }
    

    该方法 UseModelWithBatchItems() 执行以下任务:

    • 创建批处理测试数据。
    • 根据测试数据预测情绪。
    • 合并用于报告的测试数据和预测。
    • 显示预测的结果。
  2. UseModelWithSingleItem()方法调用的正下方,使用以下代码为新方法添加调用:

    UseModelWithBatchItems(mlContext, model);
    
  3. UseModelWithBatchItems()方法中添加一些注释以测试训练模型的预测:

    IEnumerable<SentimentData> sentiments = new[]
    {
        new SentimentData
        {
            SentimentText = "This was a horrible meal"
        },
        new SentimentData
        {
            SentimentText = "I love this spaghetti."
        }
    };
    

预测评论情绪

使用模型通过 Transform() 方法预测注释数据情绪:

IDataView batchComments = mlContext.Data.LoadFromEnumerable(sentiments);

IDataView predictions = model.Transform(batchComments);

// Use model to predict whether comment data is Positive (1) or Negative (0).
IEnumerable<SentimentPrediction> predictedResults = mlContext.Data.CreateEnumerable<SentimentPrediction>(predictions, reuseRowObject: false);

合并并显示预测

使用以下代码为预测创建标头:

Console.WriteLine();

Console.WriteLine("=============== Prediction Test of loaded model with multiple samples ===============");

由于 SentimentPrediction 继承自 SentimentData,因此 Transform() 使用预测字段填充 SentimentText 的方法。 随着 ML.NET 进程进程,每个组件都会添加列,这样就可以轻松地显示结果:

foreach (SentimentPrediction prediction  in predictedResults)
{
    Console.WriteLine($"Sentiment: {prediction.SentimentText} | Prediction: {(Convert.ToBoolean(prediction.Prediction) ? "Positive" : "Negative")} | Probability: {prediction.Probability} ");
}
Console.WriteLine("=============== End of predictions ===============");

Results

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

Model quality metrics evaluation
--------------------------------
Accuracy: 83.96%
Auc: 90.51%
F1Score: 84.04%

=============== End of model evaluation ===============

=============== Prediction Test of model with a single sample and test dataset ===============

Sentiment: This was a very bad steak | Prediction: Negative | Probability: 0.1027377
=============== End of Predictions ===============

=============== Prediction Test of loaded model with a multiple samples ===============

Sentiment: This was a horrible meal | Prediction: Negative | Probability: 0.1369192
Sentiment: I love this spaghetti. | Prediction: Positive | Probability: 0.9960636
=============== End of predictions ===============

=============== End of process ===============
Press any key to continue . . .

祝贺! 现已成功构建机器学习模型,用于对消息情绪进行分类和预测。

生成成功的模型是一个迭代过程。 此模型的初始质量较低,因为本教程使用小型数据集来提供快速模型训练。 如果对模型质量不满意,可以通过提供更大的训练数据集或为每个算法选择具有不同 超参数 的不同训练算法来尝试改进它。

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

后续步骤

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

  • 创建控制台应用程序
  • 准备数据
  • 加载数据
  • 生成和训练模型
  • 评估模型
  • 使用模型进行预测
  • 查看结果

转到下一教程,了解详细信息