ML.NET案例详解:在.NET下使用机器学习API实现化学分子式数据格式的判定

本文主要是介绍ML.NET案例详解:在.NET下使用机器学习API实现化学分子式数据格式的判定,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

半年前写过一篇类似的文章,题目是:《在.NET中使用机器学习API(ML.NET)实现化学分子式数据格式的判定》,在该文中,我介绍了化学分子式数据格式的基本知识,同时给出了一个案例,展示了如何在.NET/.NET Core中,使用微软开源的ML.NET框架,通过机器学习,实现化学分子式数据格式的预测。

时隔半年,ML.NET有了很大的发展。在阅读我之前那篇文章的时候,或许还会对给出的案例代码有些疑问,ML.NET经过几个版本的更新之后,API的设计变得更为合理易用,所开放的接口也越来越多(比如,新版本的ML.NET中,对机器学习引擎的OutputSchema进行了完全开放,开发者可以根据自己的需要进行调用),因此,本文就再一次回到这个话题并进行更为详细的介绍,用新版本的ML.NET重新实现化学分子式数据格式的判定。

有关化学分子式的相关知识,在这里也就不多说了,直接看代码实现部分。

准备数据

我们的数据仍然是一个CSV文件,通过逗号分隔,文件包含两个字段:结构式数据(ChemicalStructure),以及该结构式数据的类型(Type),以下是这个文件的部分片段,注意,在这个文件中,我们没有定义CSV头,不过这不重要,只要记得在后面的代码实现中,将这个设置体现出来就可以了。


[O-]C(CCCCCCCCCCCCCCCCC)=O.[Na+],SMILES
O=C(C1)N(C2[C@@]3(CC4)[C@](N4C5)([H])C[C@@]6([H])C5=CCOC1[C@]62[H])C7=C3C=CC=C7.O[N+]([O-])=O,SMILES
O=C1CC2C(C3[C@]45C(C=CC=C6)=C6N31)C(CC4N(CC5)C7)C7=CCO2.OS(O)(=O)=O.O=C8CC9C(C%10[C@@]%11%12C(C=CC=C%13)=C%13N%108)C(CC%11N(CC%12)C%14)C%14=CCO9,SMILES
C=CC1=CC=CC=C1,SMILES
N=C(OC)CCCCCCC(OC)=N.Cl.Cl,SMILES
NC(CCC(N)=O)=O,SMILES
O=C(O)C1(N(CCOC)CCOC)CCC(C)CC1,SMILES
CN(C)C(C)CC(C1=CC=CC=C1)(C(CC)=O)C2=CC=CC=C2,SMILES
NCC1(CCC(CCC)CC1)N(C)CC2=COC=C2,SMILES
AAADceByOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAACBThgAYCCAMABAAIAACQCAAAAAAAAAAAAAEIAAACABQAgAAHAAAFIAAQAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==,BASE64_CDX
AAADceByOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAACAAACBThgAYCCAMABgAIAACQCAAAAAAAAAAAAAEIAAACABQAgAAHQAAFIAAQAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==,BASE64_CDX
AAADccBCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAQCAAACBThgAYCAABAAgAAAAAAAAAAAAAAAAAAAIAAAAACEAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==,BASE64_CDX
AAADccBjgAAAAAAAAAAAAAAAAAAAAWAAAAAsAAAAAAAAAFgB+AAAHAAQAAAACAjBFwQH8L9MEACgAQZhZACAgC0REKABUCAoVBCASABASEAUBAgIAALAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==,BASE64_CDX
AAADceB7uAAAAAAAAAAAAAAAAAAAAAAAAAAwQIAAAAAAAACBAAAAHgAQCAAADCjBmAYxyIPAAgCoAiXS/ACCAAElAgAJiIGIZMiKYDLA1bGUYQhslgLYyce8rwCeCAAAAAAAAAAQAAAAAAAAAAAAAAAAAA==,BASE64_CDX
OC1=C(C2=C(C=C1)C[C@@]3([C@]45[H])[H])O[C@]([C@@]52CCN3C)([C@H](CC4)OC)[H],SMILES
OC1=C(O2)C([C@]([C@]2(C)C(CC3)=O)(CCN4C)[C@]3([H])[C@H]4C5)=C5C=C1,SMILES
........

注意:你不需要将这些数据复制下来,本文结尾会给出源代码,其中包含了这个完整的数据文件。

实现过程

可以基于.NET Framework 4.6.1或者.NET Core创建一个新的控制台应用程序,在这个控制台应用程序上,添加对ML.NET NuGet包的引用。实现的第一步就是定义我们的样本数据对象。根据上面的CSV文件结构,我们可以设计如下的类:


public class ChemicalData
{
     [Column( "0" )]
     public string ChemicalStructure;
     [Column( "1" )]
     public string Type;
}

这个类非常简单,仅仅是针对CSV文件两个列的映射。接下来,我们需要定义用于保存预测结果的数据对象,该对象不仅会用来保存预测结果值,而且还会提供基于不同分类的可信度得分(Score):


public class ChemicalDataPrediction
{
     [ColumnName( "PredictedLabel" )]
     public string Type;
     public float [] Score;
}

OK,到这里我们基本上已经清楚我们的机器学习应用场景了:我们在使用Multi-class Classification对化学结构式数据进行分类。在机器学习的应用过程中,了解应用场景是非常重要的。然后,回到Main函数,实现如下代码:


static void Main( string [] args)
{
     // 创建机器学习上下文实例
     var mlContext = new MLContext();
     // 从data.txt读入样本数据
     var dataView = mlContext.Data.ReadFromTextFile( "data.txt" , new TextLoader.Arguments
     {
         Separators = new char [] { ',' }, // 逗号分隔
         HasHeader = false , // 文件中不包含CSV头信息
         Column = new [] {
             new TextLoader.Column( "ChemicalStructure" , DataKind.Text, 0),  // 化学结构式数据字段
             new TextLoader.Column( "Type" , DataKind.Text, 1)  // 化学结构式数据类型字段
         }
     });
     // 创建机器学习管道,指定我们需要使用CSV文件中的Type字段进行标记并分类
     var pipeline = mlContext.Transforms.Conversion.MapValueToKey( "Label" , "Type" )
         
         // 指定将由ChemicalStructure字段提供特征信息
         .Append(mlContext.Transforms.Text.FeaturizeText( "Features" , "ChemicalStructure" ))
         // 选择机器学习算法
         .Append(mlContext.MulticlassClassification.Trainers.LogisticRegression())
         // 计算结果将输出到由PredictedLabel所标记的对象字段上
         .Append(mlContext.Transforms.Conversion.MapKeyToValue( "PredictedLabel" ));
     // 基于样本数据和所选择的管道选项,进行模型训练,并返回模型
     var model = pipeline.Fit(dataView);
     // 创建预测引擎
     var engine = model.CreatePredictionEngine<ChemicalData, ChemicalDataPrediction>(mlContext);
     // 对给定的测试数据进行预测,并输出测试结果
     var sample = new ChemicalData { ChemicalStructure = "NC(C(N)=O)=O" };
     var prediction = engine.Predict(sample);
     Console.WriteLine(prediction.Type);
}

代码非常简单,有几个点说明一下:

  • 新的ML.NET需要创建MLContext对象,所有的机器学习工作都会依赖于这个上下文

  • 通过MapValueToKey方法来指定读入数据的哪个字段是用来进行分类标记的,这个Label是ML.NET的一个保留字段名,在模型训练的时候会找到由Label所标记的字段进行计算

  • Features也是ML.NET的一个保留字段名,它指定了哪个(或哪些)字段将提供特征数据

  • PredictedLabel也是ML.NET的保留字段名,它指定了计算结果应该输出到哪个对象字段中

直接运行程序,可以看到,程序毫无悬念地输出了正确结果:

640?wx_fmt=png

可信度得分的获取

在上面的代码中,如果我们将断点设置在最后一句Console.WriteLine方法上,然后调试程序,查看prediction的数值,会发现,各个分类的可信度已经在Score字段里了:

640?wx_fmt=png

可问题是,我如何知道某个得分到底是属于哪个分类呢?在ML.NET 0.6之前的版本,在训练好的模型对象上,会有一个TryGetScoreLabelNames的扩展方法,它能够返回可信度得分的分类名称,顺序和Score数组的顺序一致。但从ML.NET 0.6开始,这个扩展方法已经没有了,但这并不是说ML.NET变得更弱了,相反,新版本中直接将OutputSchema对象暴露出来,开发者可以自己实现所需的方法。下面的代码展示了如何基于预测引擎的OutputSchema来获取各个分类的名称,以及所对应的可信度得分:


static void Main( string [] args)
{
     // ...
     // 接上文代码
     
     var outputSchema = engine.OutputSchema;
     TryGetScoreLabelNames(outputSchema, out var names);
     var confidences = new Dictionary< string , float >();
     for ( var idx = 0; idx < names.Length; idx++)
     {
         confidences.Add(names[idx], prediction.Score[idx]);
     }
     Console.WriteLine(JsonConvert.SerializeObject(
         new
         {
             Label = prediction.Type,
             Confidences = confidences
         },
         Formatting.Indented));
}
static bool TryGetScoreLabelNames(Schema outputSchema, out string [] names, string scoreColumnName = DefaultColumnNames.Score)
{
     names = ( string []) null ;
     var scoreColumn = outputSchema.GetColumnOrNull(scoreColumnName);
     var slotNames = new VBuffer<ReadOnlyMemory< char >>();
     scoreColumn.Value.GetSlotNames( ref slotNames);
     names = new string [slotNames.Length];
     var num = 0;
     foreach ( var denseValue in slotNames.DenseValues())
     {
         names[num++] = denseValue.ToString();
     }
     return true ;
}

再次执行程序,可以看到,我们已经可以输出各个分类的可信度得分了:

640?wx_fmt=png

预测失误

现在我们做个试验,将最后用于测试的数据从SMILES换成INCHI,比如:

1
var sample = new ChemicalData { ChemicalStructure = "InChI=1S/ClH/h1H/p-1" };

然后再次运行程序,结果发现,我们本想得到INCHI的输出,却仍然得到SMILES的结果,只不过SMILES的可信度降低了,InChi的可信度升高了:

640?wx_fmt=png

这个问题主要是因为我们所提供的用于训练的样本数据还不够多,如果训练数据量大,并且干扰比较小的话,得到的预测结果就会更准确。因此,在实践机器学习的过程中,保证训练数据的纯净度和数据量是非常重要的,这也就是为什么目前机器学习的项目中,在数据清洗这一步中有着相当大的投入。回到我们的案例,让我们在样本CSV文件中多加一些InChi数据,来帮助机器学习得到更精确的结果:


"InChI=1/C2H6O/c1-2-3/h3H,2H2,1H3",InChi
"InChI=1/C6H8O6/c7-1-2(8)5-3(9)4(10)6(11)12-5/h2,5,7-10H,1H2/t2-,5+/m0/s1",InChi
"InChI=1S/C6H8O6/c7-1-2(8)5-3(9)4(10)6(11)12-5/h2,5,7-10H,1H2/t2-,5+/m0/s1",InChi
"InChI=1S/CH4/h1H4",InChi
"InChI=1S/C2H6/c1-2/h1-2H3",InChi
"InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3",InChi
"InChI=1S/C3H7NO2/c1-2(4)3(5)6/h2H,4H2,1H3,(H,5,6)/t2-/m0/s1",InChi
"InChI=1S/ClH/h1H/p-1",InChi
"InChI=1S/C6H7NO/c1-5-3-2-4-7-6(5)8/h2-4H,1H3,(H,7,8)",InChi
"InChI=1S/CH2N2/c1-3-2/h1H2",InChi
"InChI=1S/C7H5N3O/c11-7-5-3-1-2-4-6(5)8-10-9-7/h1-4H,(H,8,9,11)",InChi
"InChI=1S/C8H6N2O/c11-8-6-3-1-2-4-7(6)9-5-10-8/h1-5H,(H,9,10,11)",InChi
"InChI=1S/C2H6N2O/c1-4(2)3-5/h1-2H3",InChi
"InChI=1S/C9H8N2O/c1-6-10-8-5-3-2-4-7(8)9(12)11-6/h2-5H,1H3,(H,10,11,12)",InChi
"InChI=1S/C6H8O/c1-2-3-4-5-6-7/h2-6H,1H3/b3-2+,5-4+",InChi

再次运行程序,我们已经可以得到正确的输出了(虽然它仍然认为有31%的可能性是SMILES):

640?wx_fmt=png

模型的保存与使用

我们可以用下面的代码将训练好的模型保存到本地ZIP文件中,以便今后直接在项目中使用:


using ( var fs = new FileStream( "ml_model.zip" , FileMode.Create, FileAccess.Write, FileShare.Write))
{
     mlContext.Model.Save(model, fs);
}

然后使用下面的代码,读入保存的模型,并进行新的预测:


var mlContext2 = new MLContext();
ITransformer loadedModel;
using ( var stream = new FileStream( "ml_model.zip" , FileMode.Open, FileAccess.Read, FileShare.Read))
{
     loadedModel = mlContext2.Model.Load(stream);
     var engine2 = loadedModel.CreatePredictionEngine<ChemicalData, ChemicalDataPrediction>(mlContext2);
     var pred = engine2.Predict( new ChemicalData { ChemicalStructure = "c1ccccc1" });
     Console.WriteLine(pred.Type);
}

总结

本文再一次介绍了如何使用微软开源的ML.NET框架,实现化学结构式数据格式的预测和判定。本文对使用ML.NET的整个流程进行了详细完整的介绍,但只演示了Multi-class Classification的应用场景。其它应用场景其实也大同小异,开发人员需要根据实际情况进行选择。通过ML.NET产生的训练模型是可以序列化到ZIP文件的,因此,模型可以方便地重用。ML.NET支持.NET Core,因此,基于docker和ASP.NET Core实现机器学习的RESTful API也是轻而易举的事情,本文就不继续深入了。

源代码下载

请 下载本文案例的源代码http://sunnycoding.cn/archives/ML_ChemStructure_Demo.zip

原文地址:http://sunnycoding.cn/2019/02/22/categorize-chemical-structure-using-ml-net-advanced/

 

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


这篇关于ML.NET案例详解:在.NET下使用机器学习API实现化学分子式数据格式的判定的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/356941

相关文章

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

SQL注入漏洞扫描之sqlmap详解

《SQL注入漏洞扫描之sqlmap详解》SQLMap是一款自动执行SQL注入的审计工具,支持多种SQL注入技术,包括布尔型盲注、时间型盲注、报错型注入、联合查询注入和堆叠查询注入... 目录what支持类型how---less-1为例1.检测网站是否存在sql注入漏洞的注入点2.列举可用数据库3.列举数据库

Linux之软件包管理器yum详解

《Linux之软件包管理器yum详解》文章介绍了现代类Unix操作系统中软件包管理和包存储库的工作原理,以及如何使用包管理器如yum来安装、更新和卸载软件,文章还介绍了如何配置yum源,更新系统软件包... 目录软件包yumyum语法yum常用命令yum源配置文件介绍更新yum源查看已经安装软件的方法总结软

Linux alias的三种使用场景方式

《Linuxalias的三种使用场景方式》文章介绍了Linux中`alias`命令的三种使用场景:临时别名、用户级别别名和系统级别别名,临时别名仅在当前终端有效,用户级别别名在当前用户下所有终端有效... 目录linux alias三种使用场景一次性适用于当前用户全局生效,所有用户都可调用删除总结Linux

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例: