使用ML.NET实现情感分析[新手篇]

在发出《.NET Core玩转机器学习》和《使用ML.NET预测纽约出租车费》两文后,相信读者朋友们即使在不明就里的情况下,也能按照内容顺利跑完代码运行出结果,对使用.NET Core和ML.NET,以及机器学习的效果有了初步感知。得到这些体验后,那么就需要回头小结一下了,本文仍然基于一个情感分析的案例,以刚接触机器学习的.NET开发者的视角,侧重展开一下起手ML.NET的基本理解和步骤。

当我们意识到某个现实问题超出了传统的模式匹配能力范围,需要借助模拟的方式先尽可能还原已经产生的事实(通常也称为拟合),然后复用这种稳定的模拟过程(通常也称为模型),对即将发生的条件进行估计,求得发生或不发生相同结果的概率,此时就是利用机器学习最好的机会,同时也要看到,这也是机器学习通常离不开大量数据的原因,历史数据太少,模拟还原这个过程效果就会差很多,自然地,评估的结果误差就大了。所以在重视数据的准确性、完整性的同时,要学会经营数据的体量出来。

若要使用机器学习解决问题,一般会经历以下这些步骤:

1. 描述问题产生的场景

2. 针对特定场景收集数据

3. 对数据预处理

4. 确定模型(算法)进行训练

5. 对训练好的模型进行验证和调优

6. 使用模型进行预测分析

 接下来我将用案例逐一介绍。 

描述问题产生的场景


说到情感分析,我假定一个最简单的句子表达的场景,就是当看到一句话,通过特定的词语,我们能判断这是一个正向积极的态度,或是负面消极的。比如“我的程序顺利通过测试啦”这就是一个正向的,而“这个函数的性能实在堪忧”就是一个负面的表达。所以,对词语的鉴别就能间接知道说这句话的人的情感反应。(本案例为降低理解的复杂程度,暂不考虑断句、重音、标点之类的这些因素。)

 

针对特定场景收集数据


为了证实上面的思路,我们需要先收集一些有用的数据。其实这也是让众多开发者卡住的环节,除了使用爬虫和自己系统中的历史数据,往往想不到短时间还能在哪获取到。互联网上有不少学院和机构,甚至政府都是有开放数据集提供的,推荐两处获取比较高质量数据集的来源:

UC Irvine Machine Learning Repository来自加州大学

kaggle.com一个著名的计算科学与机器学习竞赛网站

这次我从UCI找到一个刚好只是每行有一个句子加一个标签,并且标签已标注好每个句子是正向还是负向的数据集了。在Sentiment Labelled Sentences Data Set下载。格式类似如下:

A very, very, very slow-moving, aimless movie about a distressed, drifting young man.  0

Not sure who was more lost - the flat characters or the audience, nearly half of whom walked out.  0

Attempting artiness with black & white and clever camera angles, the movie disappointed - became even more ridiculous - as the acting was poor and the plot and lines almost non-existent.  0

Very little music or anything to speak of.  0

The best scene in the movie was when Gerardo is trying to find a song that keeps running through his head.  1

The rest of the movie lacks art, charm, meaning... If it's about emptiness, it works I guess because it's empty.  0

Wasted two hours.  0

...

观察每一行,一共是Tab分隔的两个字段,第一个字段是句子,一般我们称之为特征(Feature),第二个字段是个数值,0表示负向,1表示正向,一般我们称之为目标或标签(Label),目标值往往是人工标注的,如果没有这个,是无法使用对历史数据进行拟合这种机器学习方式的。所以,一份高质量的数据集对人工标注的要求很高,要尽可能准确。

 

对数据预处理


对于创建项目一系列步骤,参看我开头提到的两篇文章即可,不再赘述。我们直接进入正题,ML.NET对数据的处理以及后面的训练流程是通用的,这也是为了以后扩展到其他第三方机器学习包设计的。首先观察数据集的格式,创建与数据集一致的结构,方便导入过程。LearningPipeline类专门用来定义机器学习过程的对象,所以紧接着我们需要创建它。代码如下:

const string _dataPath = @".\data\sentiment labelled sentences\imdb_labelled.txt";

const string _testDataPath = @".\data\sentiment labelled sentences\yelp_labelled.txt";


public class SentimentData

{

    [Column(ordinal: "0")]

    public string SentimentText;

    [Column(ordinal: "1", name: "Label")]

    public float Sentiment;

}


var pipeline = new LearningPipeline();

pipeline.Add(new TextLoader<SentimentData>(_dataPath, useHeader: false, separator: "tab"));

pipeline.Add(new TextFeaturizer("Features", "SentimentText"));

SentimentData就是我需要的导入用的数据结构,可以看到,Column属性除了指示对应数据集的行位置,额外的对应最后一列,表示正向还是负向的字段,还要指定它是目标值,并取了个标识名。TextLoader就是专门用来导入文本数据的类,TextFeaturizer就是指定特征的类,因为每一行数据不是每一个字段都可以成为特征的,如果有较多字段时,可以在此处特别地指定出来,这样不会被无关的字段影响。

 

确定模型(算法)进行训练


本案例目标是一个0/1的值类型,换句话说恰好是一个二分类问题,因此模型上我选择了FastTreeBinaryClassifier这个类,如果略有了解机器学习的朋友一定知道逻辑回归算法,与之在目的上大致相似。若要定义模型,同时要指定一个预测用的结构,这样模型就会按特定的结构输出模型的效果,一般这个输出用的结构至少要包含目标字段。代码片段如下:

public class SentimentPrediction

{

    [ColumnName("PredictedLabel")]

    public bool Sentiment;

}


pipeline.Add(new FastTreeBinaryClassifier() { NumLeaves = 5, NumTrees = 5, MinDocumentsInLeafs = 2 });


PredictionModel<SentimentData, SentimentPrediction> model = pipeline.Train<SentimentData, SentimentPrediction>();

对训练好的模型进行验证和调优


在得到模型后,需要用测试数据集进行验证,看看拟合的效果是不是符合预期,BinaryClassificationEvaluator就是FastTreeBinaryClassifier对应的验证用的类,验证的结果用BinaryClassificationMetrics类保存。代码片段如下:

var testData = new TextLoader<SentimentData>(_testDataPath, useHeader: false, separator: "tab");

var evaluator = new BinaryClassificationEvaluator();

BinaryClassificationMetrics metrics = evaluator.Evaluate(model, testData);

Console.WriteLine();

Console.WriteLine("PredictionModel quality metrics evaluation");

Console.WriteLine("------------------------------------------");

Console.WriteLine($"Accuracy: {metrics.Accuracy:P2}");

Console.WriteLine($"Auc: {metrics.Auc:P2}");

Console.WriteLine($"F1Score: {metrics.F1Score:P2}");

像Accuracy,Auc,F1Score都是一些常见的评价指标,包含了正确率、误差一类的得分,如果得分很低,就需要调整前一个步骤中定义模型时的参数值。详细的解释参考:Machine learning glossary

 

使用模型进行预测分析


训练好一个称心如意的模型后,就可以正式使用了。本质上就是再取来一些没有人工标注结果的数据,让模型进行分析返回一个符合某目标值的概率。代码片段如下:

IEnumerable<SentimentData> sentiments = new[]

{

    new SentimentData

    {

        SentimentText = "Contoso's 11 is a wonderful experience",

        Sentiment = 0

    },

    new SentimentData

    {

        SentimentText = "The acting in this movie is very bad",

        Sentiment = 0

    },

    new SentimentData

    {

        SentimentText = "Joe versus the Volcano Coffee Company is a great film.",

        Sentiment = 0

    }

};

IEnumerable<SentimentPrediction> predictions = model.Predict(sentiments);

Console.WriteLine();

Console.WriteLine("Sentiment Predictions");

Console.WriteLine("---------------------");


var sentimentsAndPredictions = sentiments.Zip(predictions, (sentiment, prediction) => (sentiment, prediction));

foreach (var item in sentimentsAndPredictions)

{

    Console.WriteLine($"Sentiment: {item.sentiment.SentimentText} | Prediction: {(item.prediction.Sentiment ? "Positive" : "Negative")}");

}

运行结果可以看到,其分类是符合真实判断的。尽管验证阶段的得分不高,这也是很正常的,再没有任何调优下,存在一些中性、多义的句子干扰预测导致的。

 640?wx_fmt=png

这样,再有新的句子就可以放心地通过程序自动完成分类了,是不是很简单!希望本文能带给.NET开发的朋友们对ML.NET跃跃欲试的兴趣。

顺便提一下,微软Azure还有一个机器学习的在线工作室,链接地址为:https://studio.azureml.net/,相关的AI项目库在:https://gallery.azure.ai/browse,对于暂时无法安装本地机器学习环境,以及找不到练手项目的朋友 ,不妨试试这个。

最后放出项目的文件结构以及完整的代码:

640?wx_fmt=png

using System;

using Microsoft.ML.Models;

using Microsoft.ML.Runtime;

using Microsoft.ML.Runtime.Api;

using Microsoft.ML.Trainers;

using Microsoft.ML.Transforms;

using System.Collections.Generic;

using System.Linq;

using Microsoft.ML;


namespace SentimentAnalysis

{

    class Program

    {

        const string _dataPath = @".\data\sentiment labelled sentences\imdb_labelled.txt";

        const string _testDataPath = @".\data\sentiment labelled sentences\yelp_labelled.txt";


        public class SentimentData

        {

            [Column(ordinal: "0")]

            public string SentimentText;

            [Column(ordinal: "1", name: "Label")]

            public float Sentiment;

        }


        public class SentimentPrediction

        {

            [ColumnName("PredictedLabel")]

            public bool Sentiment;

        }


        public static PredictionModel<SentimentData, SentimentPrediction> Train()

        {

            var pipeline = new LearningPipeline();

            pipeline.Add(new TextLoader<SentimentData>(_dataPath, useHeader: false, separator: "tab"));

            pipeline.Add(new TextFeaturizer("Features", "SentimentText"));

            pipeline.Add(new FastTreeBinaryClassifier() { NumLeaves = 5, NumTrees = 5, MinDocumentsInLeafs = 2 });


            PredictionModel<SentimentData, SentimentPrediction> model = pipeline.Train<SentimentData, SentimentPrediction>();

            return model;

        }


        public static void Evaluate(PredictionModel<SentimentData, SentimentPrediction> model)

        {

            var testData = new TextLoader<SentimentData>(_testDataPath, useHeader: false, separator: "tab");

            var evaluator = new BinaryClassificationEvaluator();

            BinaryClassificationMetrics metrics = evaluator.Evaluate(model, testData);

            Console.WriteLine();

            Console.WriteLine("PredictionModel quality metrics evaluation");

            Console.WriteLine("------------------------------------------");

            Console.WriteLine($"Accuracy: {metrics.Accuracy:P2}");

            Console.WriteLine($"Auc: {metrics.Auc:P2}");

            Console.WriteLine($"F1Score: {metrics.F1Score:P2}");

        }


        public static void Predict(PredictionModel<SentimentData, SentimentPrediction> model)

        {

            IEnumerable<SentimentData> sentiments = new[]

            {

                new SentimentData

                {

                    SentimentText = "Contoso's 11 is a wonderful experience",

                    Sentiment = 0

                },

                new SentimentData

                {

                    SentimentText = "The acting in this movie is very bad",

                    Sentiment = 0

                },

                new SentimentData

                {

                    SentimentText = "Joe versus the Volcano Coffee Company is a great film.",

                    Sentiment = 0

                }

            };

            IEnumerable<SentimentPrediction> predictions = model.Predict(sentiments);

            Console.WriteLine();

            Console.WriteLine("Sentiment Predictions");

            Console.WriteLine("---------------------");


            var sentimentsAndPredictions = sentiments.Zip(predictions, (sentiment, prediction) => (sentiment, prediction));

            foreach (var item in sentimentsAndPredictions)

            {

                Console.WriteLine($"Sentiment: {item.sentiment.SentimentText} | Prediction: {(item.prediction.Sentiment ? "Positive" : "Negative")}");

            }

            Console.WriteLine();

        }


        static void Main(string[] args)

        {

            var model = Train();

            Evaluate(model);

            Predict(model);

        }

    }

}

相关文章: 

  • .NET Core玩转机器学习

  • 使用ML.NET预测纽约出租车费

原文地址: http://www.cnblogs.com/BeanHsiang/p/9020919.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

潘正磊:再过三五年 AI会变成开发人员的基本概念

在微软Build 2018开发者大会上&#xff0c;微软公司全球开发平台事业部的资深副总裁潘正磊&#xff08;Julia Liuson&#xff09;接受了界面记者在内的采访。潘正磊在微软西雅图总部带领一千多人组成的团队&#xff0c;微软的开发工具&#xff0c;包括Visual Studio&#xff0c…

qMISPlat入门级使用问题解答一

qMISPlat 2.0(业务配置开发平台) 自2018-4-18号正式开源以来&#xff0c;得到了众多.net core爱好者的关注&#xff0c;现将近半个月以来&#xff0c;大家反馈的一些使用配置方面的问题统一作如下解答。如你对qMISPlat不了解&#xff0c;请查看文章qMISPlat产品介绍。一、从码云…

【模拟】游戏(jzoj 1614)

游戏 题目大意&#xff1a; 有一个n*n的棋盘&#xff0c;有一个坐标在x,y的棋子&#xff0c; 1、2号玩家可以将他向左&#xff0c;向下&#xff0c;向左下&#xff08;45∘45^{\circ}45∘&#xff09;移动若干格&#xff0c;假如他们都是AKIOI聪明绝顶的巨佬&#xff0c;请问…

P4593-[TJOI2018]教科书般的亵渎【拉格朗日差值】

正题 题目链接:https://www.luogu.com.cn/problem/P4593 题目大意 场上有若干只怪&#xff0c;最高的为nnn&#xff0c;每个怪血量不同&#xff0c;有mmm个血量不存在。 不停释放亵渎&#xff08;全场打一&#xff0c;如果有怪死亡就再次生效&#xff09;&#xff0c;每次一…

如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包

MSBuild 的 Task 为我们扩展项目的编译过程提供了强大的扩展性&#xff0c;它使得我们可以用 C# 语言编写扩展&#xff1b;利用这种扩展性&#xff0c;我们可以为我们的项目定制一部分的编译细节。NuGet 为我们提供了一种自动导入 .props 和 .targets 的方法&#xff0c;同时还…

Platform.Uno介绍

编者语&#xff1a;Xamarin国内很多人说缺乏可用的实例&#xff0c;我在写书过程中在完善一些常用场景的例子&#xff0c;希望帮到大家。Build 2018结束一周了&#xff0c;善友问我要不要谈谈Xamarin的一些变化&#xff0c;但碍于时间有限一直没有付诸行动。想想总得写点什么给…

ASP.NET Core amp; Docker 实战经验分享

一.前言最近一直在研究和实践ASP.NET Core、Docker、持续集成。在ASP.NET Core 和 Dcoker结合下遇到了一些坑&#xff0c;在此记录和分享&#xff0c;希望对大家有一些帮助。二.中间镜像我前面写过一个 《ASP.NET Core & Docker 零基础持续集成 》的教程。里面我们通过持续…

【图论】【最短路】【SPFA】香甜的黄油 Sweet Butter (luogu 1828)

香甜的黄油 Sweet Butter luogu 1828 题目大意&#xff1a; 有n头奶牛&#xff0c;他们在不同的牧场中&#xff0c;他们之间有一些路&#xff0c;现在要让他们去一个地方吃黄油&#xff0c;使他们的总距离最小 题目描述 农夫John发现做出全威斯康辛州最甜的黄油的方法&…

【dfs】简单游戏(jzoj 2121)

简单游戏 题目大意 原本有n个数字&#xff0c;第1,2个相加&#xff0c;第2&#xff0c;3个相加……第n-1,n个相加&#xff0c;由此得出一个长度为n-1的新序列&#xff0c;然后不停重复&#xff0c;最后得出一个t&#xff0c;现在给出一开始的n和t求符合的序列&#xff08;字典…

[翻译] 比较 Node.js,Python,Java,C# 和 Go 的 AWS Lambda 性能

原文: Comparing AWS Lambda performance of Node.js, Python, Java, C# and GoAWS 最近宣布他们支持了 C&#xff03; (Net Core 2.0 版本) 和 Go 语言来实现 Lambda 功能。(译者注: AWS Lambda 是 AWS 推出的 Serverless 功能&#xff0c;请参阅这里或 Serverless 相关资料)做…

【结论】立体井字棋(jzoj 2124)

立体井字棋 题目大意&#xff1a; 在一个nnn的正方体中&#xff0c;由n个格子连成一条直线的方案数&#xff08;多少种可能用n个格子连成一条直线&#xff09; 样例输入 2 样例输出 28 数据范围限制 对于30%的数据&#xff0c; n<10&#xff1b; 对于100%的数据&am…

ASP.NET Core Identity 实战(3)认证过程

如果你没接触过旧版Asp.Net Mvc中的 Authorize 或者 Cookie登陆&#xff0c;那么你一定会疑惑 认证这个名词&#xff0c;这太正式了&#xff0c;这到底代表这什么&#xff1f;获取资源之前得先过两道关卡Authentication & Authorization要想了解Identity中用户登录之后&…

简明 ASP.NET Core 手册

编者&#xff1a;在4月份推送过这篇文章 简明 ASP.NET Core 手册 &#xff0c;今天再次推荐这篇文章&#xff0c;是因为原作者更新到了新版本1.1.0&#xff0c;改动很大&#xff0c;几乎所有章节都有很大程度的调整&#xff0c;这些调整都是根据读者的建议而做&#xff0c;而且…

《你必须掌握的Entity Framework 6.x与Core 2.0》正式出版感想

前言借书正式出版之际&#xff0c;完整回顾下从写博客到写书整个历程&#xff0c;也算是对自己近三年在技术上的一个总结&#xff0c;整个历程可通过三个万万没想到来概括&#xff0c;请耐心阅读。写博、写书完整历程回顾从2013年12月注册博客园账号&#xff0c;注册博客园账号…

Entity Framework Core 2.1带来更好的SQL语句生成方案

微软发布了Entity Framework Core2.1&#xff0c;为EF开发者带来了很多期待已久的特性。EF Core 2.1增加了对SQL GROUP BY的支持&#xff0c;支持延迟加载和数据种子等。EF Core 2.1的第一个重要新增特性是将GroupBy操作符翻译成包含GROUP BY子句的SQL。缺乏这种支持被认为是EF…

【枚举】数列(jzoj 1507)

数列 题目大意&#xff1a;给出一个等差数列的初始值和差值还有一个等比数列的处值和比值&#xff0c;问不大于n的数中&#xff0c;问这两个序列中&#xff0c;有多少个数小于n&#xff08;只要在一个序列中就行了&#xff0c;在两个序列中只记一次&#xff09; 样例输入 1 …

【模拟】蚂蚁(jzoj 1508)

蚂蚁 题目大意&#xff1a; 有n只蚂蚁&#xff0c;他们各往一个方向走&#xff08;上北&#xff0c;下南&#xff0c;左西和有东四个方向&#xff09;&#xff0c;他们如果撞到一起就会直接消失&#xff0c;每个单位时间走一格&#xff0c;但有一种情况&#xff1a;两只蚂蚁相…

【结论】单元格(jzoj 1509)

单元格 题目大意&#xff1a; 在一个RC的矩形中选三个点&#xff0c;使他们行列各不同&#xff0c;定义“费用”为&#xff0c;这三个点之间的行列的差值的和&#xff08;1,2和3,4费用是差值是&#xff08;3-1&#xff09;&#xff08;4-2&#xff09;224&#xff09;&#x…

【DP】剪草(jzoj 1510)

剪草 题目大意&#xff1a; 有n棵小草&#xff0c;B某看它们很不顺眼&#xff0c;想让他们的高度总和不大于H&#xff0c;它们一开始各有一个高度&#xff0c;然后它们各有一个固定的生长值&#xff0c;B某每个单位时间可以将一棵草减掉&#xff08;让他的高度变为0&#xff…

codeforces 962E Byteland, Berland and Disputed Cities 最小生成树变形

题目 题目链接 题意 在OxOx轴上有一堆点&#xff0c;这些点有三种类型R、B、PR、B、P型&#xff0c;现在要求添加一些线段把这些点连起来&#xff0c;使得如果去掉RR类型点,剩下的点都是联通的。如果去掉B" role="presentation" style="position: rela…