【.Net 9下使用Tensorflow.net---通过LSTM实现中文情感分析】

ops/2025/3/16 16:27:33/

.Net 9下使用Tensorflow.net---通过LSTM实现中文情感分析

  • 一、创建项目,并导入各依赖项
    • 1、创建.net9的控制台应用程序
    • 2、通过nuget,导入依赖项:
      • TensorFlow.NET
      • TensorFlow.Keras
      • SciSharp.TensorFlow.Redist–如果使用GPU训练,请使用不同的依赖包
      • Jieba.NET
  • 二、处理评论集数据
    • 1、加载初始化数据 获取字典
    • 2、根据字典,对评论数据进行向量化处理
    • 3、准备训练集
  • 二、创建模型,训练模型
  • 三、 调用以上方法,得到训练后的模型
  • 四、预测

在这个例子里,使用了Tensorflow.net中的LSTM模型,加载电商评论集,经过文本拆分、向量化、构建LSTM模型训练,可以实现中文情感分析,正确率可以达到 90%以上。
本例的基本步骤为
1、导入评论数据集;
2、进行拆分,构建词典;
3、对评论数据集进行向量化处理;
4、构建模型进行训练;
5、按批次进行预测。

一、创建项目,并导入各依赖项

1、创建.net9的控制台应用程序

2、通过nuget,导入依赖项:

TensorFlow.NET

TensorFlow.Keras

SciSharp.TensorFlow.Redist–如果使用GPU训练,请使用不同的依赖包

Jieba.NET

二、处理评论集数据

1、加载初始化数据 获取字典

本文选用的是电商评论数据,该数据集的下载网址为:https://github.com/renjunxiang/Text-Classification/blob/master/TextClassification/data/data_single.csv ,示例如下:

evaluation,label
用了一段时间,感觉还不错,可以,正面
电视非常好,已经是家里的第二台了。第一天下单,第二天就到本地了,可是物流的人说车坏了,一直催,客服也帮着催,到第三天下午5点才送过来。父母年纪大了,买个大电视画面清晰,趁着耳朵还好使,享受几年。,正面
电视比想象中的大好多,画面也很清晰,系统很智能,更多功能还在摸索中,正面
不错,正面
用了这么多天了,感觉还不错。夏普的牌子还是比较可靠。希望以后比较耐用,现在是考量质量的时候。,正面
物流速度很快,非常棒,今天就看了电视,非常清晰,非常流畅,一次非常完美的购物体验,正面
非常好,客服还特意打电话做回访,正面
物流小哥不错,辛苦了,东西还没用,正面
送货速度快,质量有保障,活动价格挺好的。希望用的久,不出问题。,正面
非常漂亮,也非常清晰,反应速度也快。,正面
很不错家里都喜欢。。。一次买了三台,正面
送货非常快!质量非常好,这次购物非常愉快!!,正面
58好大……都不错。看质量咯,正面
物流很快,物有所值,值得信赖。依旧会关顾!谢谢商家!,正面
这价钱超值,收到货马上装上看了一下。很清晰式样也蛮好!赞赞……,正面
目前感受还不错,正面
速度快、服务态度很好,正面
挺好的,物流也快,两天就到了,正面
发货速度快,正面

加载文本后,使用结巴分词进行分词,分词后 进行每行的遍历,去掉停用词后。得到后续使用的词典,词典中包含拆分后的词和词的索引。
此处注意:
该方法中初始化以下 计算结果 :词典——word_dict,评论数据——data(包含评论文本和评论结果,评论文本是拆分后 去除停用词后,以" "相连接的字符串,评论结果 为 0、1数组,1为正面评论,0为负面评论),训练集每行长度——maxLenth(计算每行词的平均数)
为了保证后续训练及预测文本的每个词都有对应的序号,所以词典中增加了几个特殊的词,词典中找不到的,以及填充数组的词。以保证后续向量化的数组都有对应的值
方法如下:

private Dictionary<string, int> init_word_dict(string path)
{          var words = new List<string>();           var segmenter = new JiebaSegmenter();int allong = 0;data = File.ReadAllLines(path).Skip(1) // 跳过标题行.Select(line => line.Split(',')).ToList();//data中包含了所有的 评论文本 和 评论结果,此处进行乱序处理data = data.OrderBy(x => Guid.NewGuid()).ToList();//加载停用词,如果初始化文本中存在停用词,就去掉stopWords = File.ReadAllLines("D:\\workspace\\src\\stopwords.txt");for (int i = 0; i < data.Count;){string sentence = data[i][0];//使用结巴分词进行分词var cuts = segmenter.Cut(sentence.Trim());cuts = cuts.Where(d => !stopWords.Contains(d) && d.Length > 1);if (cuts.Count() > 0){allong += cuts.Count();sentences.Add(string.Join(" ", cuts));words.AddRange(cuts);                     i++;}else{data.RemoveAt(i);}}//使用平均数来定义训练集的每行最大词组数maxLenth=(int)allong/ (int)sentences.Count();//初始化词典 中包括的是 所有的拆分后的词和 词出现的频率//此处优化其实 可以去掉低频出现的词。本文中不再处理var word_counter = words.GroupBy(x => x).Select(x => new { Word = x.Key, Count = x.Count() }).OrderByDescending(x => x.Count).ToArray();//word_dict 是最终的返回的字典,包括的是 词 及词的索引var word_dict = new Dictionary<string, int>();word_dict["<pad>"] = 0;word_dict["<unk>"] = 1;word_dict["<eos>"] = 2;foreach (var word in word_counter)word_dict[word.Word] = word_dict.Count;return word_dict;
}

2、根据字典,对评论数据进行向量化处理

方法如下,比较简单,就是按照词典中的索引,转化评论数据中的词,并且按照maxLenth,截断每行的最大长度,其中返回的 y,其实就是 已经做过 one_hot处理的评论结果:

 private (int[][], NDArray) init_word_Array( Dictionary<string, int> word_dict, int document_max_len,NDArray oneHotLabels){//var contents = File.ReadAllLines(path);var x = sentences.Select(c => (c + " <eos>").Split(' ').Take(document_max_len).Select(w => word_dict.ContainsKey(w) ? word_dict[w] : word_dict["<unk>"]).ToArray()).ToArray();for (int i = 0; i < x.Length; i++){if (x[i].Length == document_max_len)x[i][document_max_len - 1] = word_dict["<eos>"];elseArray.Resize(ref x[i], document_max_len);}var y = oneHotLabels;return (x, y);}

3、准备训练集

以上准备工作基本就完成了,以下两个方法 是将 上述 返回x,y --向量化后的评论数据集和评论结果分别拆分为训练集、训练结果集,测试集和测试结果集,另一个方法 只是为了将动态数组转化为固定大小数组,以便于转化为张量

 private (NDArray, NDArray, NDArray, NDArray) train_test_split(NDArray x, NDArray y, float test_size = 0.3f){Console.WriteLine("Splitting in Training and Testing data...");long len = x.shape[0];int train_size = (int)Math.Round(len * (1 - test_size));//训练集xTrain = x[new Slice(stop: train_size), new Slice()];//测试集xTest = x[new Slice(start: train_size), new Slice()];//结果集yTrain = y[new Slice(stop: train_size)];yTest = y[new Slice(start: train_size)];Console.WriteLine("\tDONE");return (xTrain, xTest, yTrain, yTest);}public int[,] TransformArray(int[][] x,int xLength,int yLength){int[,] array=new int[xLength, yLength];for (int i = 0; i < xLength; i++){ for(int j=0;j<yLength;j++){array[i, j] = 0;}}for (int i = 0; i < x.Length; i++){for (int j = 0; j < x[i].Length; j++){array[i, j] = x[i][j];}}return array;}

二、创建模型,训练模型

使用LSTM模型,LSTM模型(Long Short-Term Memory)是一种特殊的循环神经网络(RNN),可用来处理
‌时间序列预测‌:如气象数据、股票价格预测等
自然语言处理‌:用于语言模型、机器翻译、情感分析等
一般大型的文本推理模型,使用的是LLM,而LSTM比较简单,适用于用来做少量的数据训练模型。

public IModel BuildModel(int maxLength, int embeddingDim, int vocabSize)
{var layers = keras.layers;var model = keras.Sequential(new List<ILayer> {//嵌入层layers.Embedding(vocabSize,embeddingDim,input_length:maxLength),layers.LSTM(128),//layers.Dropout(0.5f) ,  // 每次迭代丢弃50神经元 防止过拟合layers.Dense(64, activation: "relu"),layers.Dropout(0.5f) ,  // 每次迭代丢弃50神经元 防止过拟合layers.Dense(2, activation: "sigmoid"),//layers.out});return model;
}public void TrainModel(IModel model, NDArray xTrain, NDArray yTrain, NDArray xTest, NDArray yTest)
{model.compile(optimizer: keras.optimizers.Adam(), loss: keras.losses.CategoricalCrossentropy(), metrics: new[] { "accuracy" }); // 编译模型,使用Adam优化器和二元交叉熵损失函数//model.compile(optimizer: new Adam(), loss: "categorical_crossentropy", metrics: new[] { "accuracy" });model.fit(xTrain, yTrain, batch_size: 64, epochs: 5, validation_data: (xTest, yTest));           
}

三、 调用以上方法,得到训练后的模型

  public IModel PrepareData(string path){//var vocabSize = 0; // 根据实际词汇表大小调整word_dict=init_word_dict(path);labels = data.Select(d => { if (d.Length < 2) return 0;return d[1].Trim() == "正面" ? 1 : 0;}).ToList();// 构建词汇表var x = new int[0][];NDArray y;vocabulary_size = len(word_dict);           // 将标签转换为 one-hot 编码var oneHotLabels =  np.eye(2)[np.array(labels.ToArray())];(x, y) = init_word_Array(word_dict, maxLenth,oneHotLabels);划分训练集和测试集//var (xTrain, xTest, yTrain, yTest) = SplitData(paddedSequences, oneHotLabels, testSize: 0.2);var tmpX = TransformArray(x,x.Length,maxLenth);Tensor tensorX = tf.constant(tmpX, TF_DataType.TF_INT32);NDArray arrayX = tensorX.numpy();(xTrain, xTest, yTrain, yTest) = train_test_split(arrayX, y, test_size: 0.15f);var embeddingDim = 150;_model = BuildModel(maxLenth, embeddingDim, vocabulary_size);TrainModel(_model, xTrain, yTrain, xTest, yTest);return _model;}

四、预测

以下是 预测的方法 和调用预测的方法,
需要预测的评论文本 一样也要进行 向量化处理,并且 注意,预测试,需要输入批次,批次大小可以和 训练时的批次大小保持一致。

  public string PredictSentiment(string text, IModel model){var segmenter = new JiebaSegmenter();var words = segmenter.Cut(text);words = words.Where(d => !stopWords.Contains(d) && d.Length > 1);string tmpText = string.Empty;if (words.Count() > 0){tmpText=string.Join(" ", words);}else{return "负面";}int[,] wordsArray = new int[1, maxLenth];for (int i = 0; i < maxLenth; i++)wordsArray[0, i] = 0;for (int i = 0; i < words.Count(); i++){if (i >= maxLenth)break;if (word_dict.ContainsKey(words.ElementAt(i)))wordsArray[0, i] = word_dict[words.ElementAt(i)];elsewordsArray[0, i] = word_dict["<unk>"];}//var x = tmpText.Select(c => (c + " <eos>")//   .Split(' ').Take(maxLenth)//   .Select(w => word_dict.ContainsKey(w) ? word_dict[w] : word_dict["<unk>"]).ToArray())//   .ToArray();//var tmpX = TransformArray(x, x.Length, maxLenth);Tensor tensorX = tf.constant(wordsArray, TF_DataType.TF_INT32);NDArray arrayX = tensorX.numpy();//var sequence = tokenizer.TextsToSequences(new List<string> { string.Join(" ", words) })[0];//var paddedSequence = PadSequences(new List<List<int>> { sequence }, maxlen: maxLenth);var prediction = model.predict(arrayX,batch_size:64);var predict_label = np.argmax(prediction[0].numpy(), axis: 1);return predict_label[0].ToString() ;}

控制台主函数内调用预测方法

while (true) {Console.WriteLine("请输入您要分析的句子:");string input = Console.ReadLine();if (input == "break")break;var result = myConverter.PredictSentiment(input, _model);Console.WriteLine(result);
}

最终程序的输入结果如下:

训练模型 的准确率如下:
在这里插入图片描述
总体还是上升的
预测文本的结果如下:
在这里插入图片描述
准确率还是很高的。


http://www.ppmy.cn/ops/166244.html

相关文章

理解langchain langgraph 官方文档示例代码中的MemorySaver

以下是langchain v0.3官方示例代码 from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import START, MessagesState, StateGraph# 可以理解为&#xff1a;定义一个流程&#xff0c;这个流程中用到的数据类型是Messages。 <---定义一个有向图&…

Android 拍照开发——移动虚拟机摄像头

背景 最近几个月&#xff0c;承接了客户安卓拍照开发需求&#xff0c;由于开发所在区域不允许使用实体安卓设备&#xff0c;只能在安卓虚拟机上进行调试&#xff0c;在调试过程中涉及到反复拍照、移除&#xff0c;需要让虚拟机拍摄的图像有区别&#xff0c;默认情况下&#xf…

Halcon 和 opencv比有什么区别与优劣

Halcon 和 OpenCV 都是机器视觉领域的重要工具&#xff0c;但它们的设计目标、功能特点和适用场景有所不同。以下是两者的详细对比&#xff1a; 1. ​定位与目标用户 ​Halcon&#xff1a; ​定位&#xff1a;商业机器视觉软件&#xff0c;专注于工业应用。​目标用户&#xf…

注意力机制:让AI拥有“黄金七秒记忆“的魔法----(点积注意力)

注意力机制&#xff1a;让AI拥有"黄金七秒记忆"的魔法–&#xff08;点积注意力&#xff09; 注意⼒机制对于初学者来说有点难理解&#xff0c;我们⼀点⼀点地讲。现在先暂时忘记编码器、解码器、隐藏层和序列到序列这些概念。想象我们有两个张量x1和x2&#xff0c;…

Spring 事务失效的 8 种场景!

在日常工作中&#xff0c;如果对Spring的事务管理功能使用不当&#xff0c;则会造成Spring事务不生效的问题。而针对Spring事务不生效的问题&#xff0c;也是在跳槽面试中被问的比较频繁的一个问题。 点击上方卡片关注我 今天&#xff0c;我们就一起梳理下有哪些场景会导致Sp…

3. 无重复字符的最长子串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为无…

Linux内核,mmap_pgoff在mmap.c的实现

1. mmap_pgoff的系统调用实现如下 SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,unsigned long, prot, unsigned long, flags,unsigned long, fd, unsigned long, pgoff) {return ksys_mmap_pgoff(addr, len, prot, flags, fd, pgoff); }2. ksys_mma…

MyBatis框架操作数据库一>xml和动态Sql

目录 配置连接字符串和MyBatis:数据库的连接配置&#xff1a;XML的配置&#xff1a; XML编写Sql:model层&#xff1a;mapper层&#xff1a; 动态Sql:if 标签和trim标签&#xff1a;where标签:Set标签:Foreach标签: Mybatis的开发有两种方式&#xff1a;&#xff1a; 注解和XML&…