教程:在 ML.NET 中使用预先训练的 TensorFlow 模型分析电影评论的情绪
本教程演示如何使用预先训练的 TensorFlow 模型对网站评论中的情绪进行分类。 二元情绪分类器是使用 Visual Studio 开发的 C# 控制台应用程序。
本教程中使用的 TensorFlow 模型是使用 IMDB 数据库中的电影评论训练的。 完成应用程序的开发后,你将能够提供电影评论文本,应用程序将会告诉你该评论是正面情绪还是负面情绪。
在本教程中,你将了解:
- 加载预先训练的 TensorFlow 模型
- 将网站评论文本转换为适用于模型的特征
- 使用模型进行预测
可以在 dotnet/samples 存储库中找到本教程的源代码。
先决条件
- 已安装“.NET 桌面开发”工作负载的 Visual Studio 2022。
安装
创建应用程序
创建名为“TextClassificationTF”的 C# 控制台应用程序。 单击“下一步”按钮。
选择 .NET 6 作为要使用的框架。 单击“创建” 按钮。
在项目中创建名为“Data”的目录,用于保存数据集文件。
安装“Microsoft.ML NuGet 包” :
注意
除非另有说明,否则本示例使用前面提到的 NuGet 包的最新稳定版本。
在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。 选择“nuget.org”作为包源,然后选择“浏览”选项卡。搜索“Microsoft.ML”,选择所需的包,然后选择“安装”按钮 。 同意所选包的许可条款,继续执行安装。 对 Microsoft.ML.TensorFlow、Microsoft.ML.SampleUtils 和 SciSharp.TensorFlow.Redist,重复上述步骤。
将 TensorFlow 模型添加到项目
注意
本教程的模型来自 dotnet/machinelearning-testdata GitHub 存储库。 该模型采用 TensorFlow SavedModel 格式。
下载 sentiment_model zip 文件并将其解压缩。
该 zip 文件包含:
saved_model.pb
:TensorFlow 模型本身。 该模型采用表示 IMDB 评论字符串中文本的特征的固定长度(大小为 600)整数数组,并输出两个概率(总和为 1):输入评论具有正面情绪的概率,以及输入评论具有负面情绪的概率。imdb_word_index.csv
:从单个单词到整数值的映射。 映射用于为 TensorFlow 模型生成输入特征。
将最内层
sentiment_model
目录的内容复制到 TextClassificationTF 项目的sentiment_model
目录中。 此目录包含本教程所需的模型和其他支持文件,如下图所示:在“解决方案资源管理器”中,右键单击
sentiment_model
目录和子目录中的每个文件,然后选择“属性”。 在“高级”下,将“复制到输出目录”的值更改为“如果较新则复制” 。
添加 using 语句和全局变量
将以下附加的
using
语句添加到“Program.cs”文件顶部:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms;
在 using 语句后面创建一个全局变量,以保留保存的模型文件路径。
string _modelPath = Path.Combine(Environment.CurrentDirectory, "sentiment_model");
_modelPath
是已训练模型的文件路径。
为数据建模
电影评论是自由格式的文本。 应用程序会将文本转换为模型在多个离散阶段中所需的输入格式。
首先是将文本拆分为单独的单词,然后使用提供的映射文件将每个单词映射到整数编码。 这种转换的结果是一个可变长度的整数数组,其长度对应于句子中的单词数。
Property | 值 | 类型 |
---|---|---|
ReviewText | 这部电影非常不错 | string |
VariableLengthFeatures | 14,22,9,66,78,... | int[] |
然后将可变长度特征数组的大小调整为固定长度 600。 这是 TensorFlow 模型所需的长度。
Property | 值 | 类型 |
---|---|---|
ReviewText | 这部电影非常不错 | string |
VariableLengthFeatures | 14,22,9,66,78,... | int[] |
特征 | 14,22,9,66,78,... | int[600] |
在 Program.cs 文件底部为输入数据创建一个类:
/// <summary> /// Class to hold original sentiment data. /// </summary> public class MovieReview { public string? ReviewText { get; set; } }
输入数据类
MovieReview
具有用于用户评论 (ReviewText
) 的string
。在
MovieReview
类之后为可变长度特征创建一个类:/// <summary> /// Class to hold the variable length feature vector. Used to define the /// column names used as input to the custom mapping action. /// </summary> public class VariableLength { /// <summary> /// This is a variable length vector designated by VectorType attribute. /// Variable length vectors are produced by applying operations such as 'TokenizeWords' on strings /// resulting in vectors of tokens of variable lengths. /// </summary> [VectorType] public int[]? VariableLengthFeatures { get; set; } }
VariableLengthFeatures
属性的 VectorType 特性用于将其指定为向量。 所有向量元素都必须是同一类型。 在具有大量列的数据集中,将多个列作为单个向量加载会减少应用数据转换时的数据传递次数。此类在
ResizeFeatures
操作中使用。 它的属性名称(本例中只有一个)用于指示 DataView 中的哪些列可用作自定义映射操作的输入。在
VariableLength
类之后为固定长度特征创建一个类:/// <summary> /// Class to hold the fixed length feature vector. Used to define the /// column names used as output from the custom mapping action, /// </summary> public class FixedLength { /// <summary> /// This is a fixed length vector designated by VectorType attribute. /// </summary> [VectorType(Config.FeatureLength)] public int[]? Features { get; set; } }
此类在
ResizeFeatures
操作中使用。 它的属性名称(本例中只有一个)用于指示 DataView 中的哪些列可用作自定义映射操作的输出。请注意,属性
Features
的名称由 TensorFlow 模型确定。 此属性名称无法更改。在
FixedLength
类之后为预测创建一个类:/// <summary> /// Class to contain the output values from the transformation. /// </summary> public class MovieReviewSentimentPrediction { [VectorType(2)] public float[]? Prediction { get; set; } }
MovieReviewSentimentPrediction
是在训练模型后使用的预测类。MovieReviewSentimentPrediction
有一个float
数组 (Prediction
) 和一个VectorType
属性。创建另一个类来保留配置值,例如特征向量长度:
static class Config { public const int FeatureLength = 600; }
创建 MLContext、查找字典以及用于调整特征大小的操作
MLContext 类是所有 ML.NET 操作的起点。 初始化 mlContext
会创建一个新的 ML.NET 环境,可在模型创建工作流对象之间共享该环境。 从概念上讲,它与实体框架中的 DBContext
类似。
使用以下代码替换
Console.WriteLine("Hello World!")
行,以声明和初始化 mlContext 变量:MLContext mlContext = new MLContext();
通过使用
LoadFromTextFile
方法创建字典来将单词编码为整数,以便从文件中加载映射数据,如下表所示:字 Index kids 362 want 181 wrong 355 effects 302 feeling 547 添加下面的代码,创建查找映射:
var lookupMap = mlContext.Data.LoadFromTextFile(Path.Combine(_modelPath, "imdb_word_index.csv"), columns: new[] { new TextLoader.Column("Words", DataKind.String, 0), new TextLoader.Column("Ids", DataKind.Int32, 1), }, separatorChar: ',' );
添加一个
Action
,将可变长度单词整数数组的大小调整为固定大小的整数数组,再加上下面的几行代码:Action<VariableLength, FixedLength> ResizeFeaturesAction = (s, f) => { var features = s.VariableLengthFeatures; Array.Resize(ref features, Config.FeatureLength); f.Features = features; };
加载预先训练的 TensorFlow 模型
添加代码以加载 TensorFlow 模型:
TensorFlowModel tensorFlowModel = mlContext.Model.LoadTensorFlowModel(_modelPath);
加载模型后,可以提取其输入和输出架构。 所显示的架构仅供参考和学习。 最终应用程序不需要此代码即可运行:
DataViewSchema schema = tensorFlowModel.GetModelSchema(); Console.WriteLine(" =============== TensorFlow Model Schema =============== "); var featuresType = (VectorDataViewType)schema["Features"].Type; Console.WriteLine($"Name: Features, Type: {featuresType.ItemType.RawType}, Size: ({featuresType.Dimensions[0]})"); var predictionType = (VectorDataViewType)schema["Prediction/Softmax"].Type; Console.WriteLine($"Name: Prediction/Softmax, Type: {predictionType.ItemType.RawType}, Size: ({predictionType.Dimensions[0]})");
输入架构是整数编码单词的固定长度数组。 输出架构是概率的浮点数组,指示评论的情绪是负面情绪还是正面情绪。 这些值的总和为 1,因为正面情绪的概率与负面情绪的概率相互补足。
创建 ML.NET 管道
创建管道并使用 TokenizeIntoWords 转换将输入文本拆分为单词,从而将文本拆分为单词以作为下一行代码:
IEstimator<ITransformer> pipeline = // Split the text into individual words mlContext.Transforms.Text.TokenizeIntoWords("TokenizedWords", "ReviewText")
TokenizeIntoWords 转换使用空格将文本/字符串分析为单词。 它将创建一个新列,并基于用户定义的分隔符将每个输入字符串拆分为子字符串的向量。
使用你在上面声明的查找表将单词映射到其整数编码:
// Map each word to an integer value. The array of integer makes up the input features. .Append(mlContext.Transforms.Conversion.MapValue("VariableLengthFeatures", lookupMap, lookupMap.Schema["Words"], lookupMap.Schema["Ids"], "TokenizedWords"))
将可变长度整数编码调整为模型所需的固定长度:
// Resize variable length vector to fixed length vector. .Append(mlContext.Transforms.CustomMapping(ResizeFeaturesAction, "Resize"))
使用加载的 TensorFlow 模型对输入进行分类:
// Passes the data to TensorFlow for scoring .Append(tensorFlowModel.ScoreTensorFlowModel("Prediction/Softmax", "Features"))
TensorFlow 模型输出称为
Prediction/Softmax
。 请注意,名称Prediction/Softmax
由 TensorFlow 模型确定。 此名称无法更改。为输出预测创建一个新列:
// Retrieves the 'Prediction' from TensorFlow and copies to a column .Append(mlContext.Transforms.CopyColumns("Prediction", "Prediction/Softmax"));
需要将
Prediction/Softmax
列复制到其名称可用作 C# 类中的属性的列:Prediction
。 不允许在 C# 属性名称中使用/
字符。
从管道创建 ML.NET 模型
添加代码以从管道创建模型:
// Create an executable model from the estimator pipeline IDataView dataView = mlContext.Data.LoadFromEnumerable(new List<MovieReview>()); ITransformer model = pipeline.Fit(dataView);
通过调用
Fit
方法,从管道中的估算器链创建 ML.NET 模型。 在这种情况下,我们不会调整任何数据以创建模型,因为 TensorFlow 模型此前已经过训练。 我们提供一个空的数据视图对象,以满足Fit
方法的要求。
使用模型进行预测
在
MovieReview
类上方添加PredictSentiment
方法:void PredictSentiment(MLContext mlContext, ITransformer model) { }
添加以下代码以创建
PredictionEngine
作为PredictSentiment()
方法中的第一行:var engine = mlContext.Model.CreatePredictionEngine<MovieReview, MovieReviewSentimentPrediction>(model);
PredictionEngine 是一个简便 API,可使用它对单个数据实例执行预测。
PredictionEngine
不是线程安全。 可以在单线程环境或原型环境中使用。 为了在生产环境中提高性能和线程安全,请使用PredictionEnginePool
服务,这将创建一个在整个应用程序中使用的PredictionEngine
对象的ObjectPool
。 请参阅本指南,了解如何在 ASP.NET Core Web API 中使用PredictionEnginePool
。注意
PredictionEnginePool
服务扩展目前处于预览状态。通过创建一个
MovieReview
实例,在Predict()
方法中添加一个注释来测试定型模型的预测:var review = new MovieReview() { ReviewText = "this film is really good" };
通过在
PredictSentiment()
方法中添加接下来的几行代码,将测试评论数据传递到Prediction Engine
:var sentimentPrediction = engine.Predict(review);
Predict() 函数对单行数据进行预测:
Property 值 类型 预测 [0.5459937, 0.454006255] float[] 使用以下代码显示情绪预测:
Console.WriteLine($"Number of classes: {sentimentPrediction.Prediction?.Length}"); Console.WriteLine($"Is sentiment/review positive? {(sentimentPrediction.Prediction?[1] > 0.5 ? "Yes." : "No.")}");
在调用
Fit()
方法之后添加对PredictSentiment
的调用:PredictSentiment(mlContext, model);
结果
生成并运行应用程序。
结果应如下所示。 处理期间将显示消息。 你可能会看到警告或处理消息。 为简便起见,已从以下结果中删除这些消息。
Number of classes: 2
Is sentiment/review positive ? Yes
祝贺你! 现已通过在 ML.NET 中重用预先训练的 TensorFlow
模型成功生成用于分类和预测消息情绪的机器学习模型。
可以在 dotnet/samples 存储库中找到本教程的源代码。
在本教程中,你将了解:
- 加载预先训练的 TensorFlow 模型
- 将网站评论文本转换为适用于模型的特征
- 使用模型进行预测