Infer.NET 是开放源代码的代码库,可用于创建概率性编程系统。我往往会将普通的计算机程序视作,主要基于有指定类型的值的变量(如有值“Q”的 char 变量)。概率性编程主要基于概率分布,如平均值为 0.0 且标准偏差为 1.0 的高斯分布。

本文通过使用正面交锋的结果计算一组竞争对手的分级,介绍了如何开始使用 Infer.NET 库。若要了解概率性编程以及本文的论述方向,最好查看图 1 中的演示程序。此演示程序设置了六个竞争对手(想想运动队):Angels、Bruins、Comets、Demons、Eagles 和 Flyers。

图 1:Infer.NET 分级实际效果

六支运动队相互比赛。每队比赛三场,所以共有九场比赛。此演示程序定义了概率性模型,假定每支运动队都有固有实力,每种固有实力都可以用平均值为 2000 且标准偏差为 200 任意单位的高斯分布(亦称为“正态分布”或“钟形分布”)进行描述。

此演示程序使用输赢数据推断出六支运动队的实力。Angels 三场比赛全胜,它的推断实力为 2256.8 个单位,比假定的 2000 个单位平均实力高出约 1.25 个标准偏差单位。Flyers 三场比赛全败,它的推断实力为 1739.7 个单位。

虽然这里我使用的是“实力”这个词,但你可以将推断数值视为分级。请注意,如果可以推断一组项的分级,就会自动获得这些项的排名。最终排名(按从最好到最差的顺序)是 Angels、Bruins、Comets、Eagles、Demons 和 Flyers。

若要更好地理解本文,至少必须拥有中等水平或更好的 C# 编程技能,但无需对 Infer.NET 或概率性编程有任何了解。Infer.NET 仅支持 C# 和 F#,所以可以视需要将此演示程序重构为 F#。了解概率性编程的基础知识后,便能使用其他许多概率性编程框架(如 Stan 或 Edward)之一重写此演示程序了。

本文展示了此演示程序的完整源代码。也可以在下载的随附文件中找到源代码。为了尽可能地让主要思想清晰明确,已删除所有常见错误检查。

了解随机变量

此演示程序假定每支运动队的实力是高斯分布的随机变量,具有指定的平均值和标准偏差。这究竟是什么意思?这个假定又源自何处?

随机变量分布有很多种类型。每种类型都有一个或多个特征参数。例如,如果随机变量遵循均匀分布,且 a = 2.0、b = 5.0,那么它可以是介于 2.0 和 5.0 之间的任何值,其中每个可能值的可能性都相等。对于此演示程序问题,可以假定运动队实力均匀分布,且 a = 1000、b = 3000。也就是说,此类实力的平均值为 0.5 * (1000 + 3000) = 2000.0 个单位,标准偏差为 sqrt((1/12) * (3000 - 1000)^2) = 577.35 个单位。

此演示程序假定运动队实力为,平均值 = 2000 且标准偏差 = 200 的高斯分布。因此,此类运动队的平均实力约为 2000,并且约 99% 的此类运动队的实力介于 2000 - (3 * 200) = 1400 和 2000 + (3 * 200) = 2600 之间。

此时,你可能会想,“太棒了,所以我必须获得统计分布博士学位,才能使用 Infer.NET 并创建概率性程序。” 这种想法并不完全正确。Infer.NET 支持多种分布,但在实践中,通常只需要了解一小部分。我最常使用的几个分布是,高斯分布、均匀分布、贝塔分布、二项分布、多项分布、伽玛分布和泊松分布。就好比是,.NET Framework 的 System.Collections 命名空间支持的许多类型的数据结构。尽管数据结构有很多,但通常只使用几种(堆栈、队列、字典);遇到需要使用新数据结构解决的问题时,就会学习新数据结构。

平均值是数据平均值,标准偏差是用于度量数据分布情况。方差是标准偏差的平方,精度是方差的倒数。之所以用三个包含完全相同信息的不同术语是因为,在用于某种数学方程时,有时一种形式会比其他形式更方便。Infer.NET 库往往使用方差和精度,而不是标准偏差。

演示程序结构

图 2 展示了此演示程序的完整源代码(为节省空间,进行了少量小幅改动)。

图 2:完整源代码

using System;using Microsoft.ML.Probabilistic.Models;using Microsoft.ML.Probabilistic.Algorithms;using Microsoft.ML.Probabilistic.Distributions;// VS2017 (Framework 4.7) Infer.NET 0.3.1810.501namespace InferStrengths{  class InferStrengthsProgram  {    static void Main(string[] args)    {      Console.WriteLine("Begin Infer.NET demo ");      // ===== Set up teams and win-loss data =====================      string[] teamNames = new string[] { "Angels", "Bruins",        "Comets", "Demons", "Eagles", "Flyers" };      int N = teamNames.Length;       int[] winTeamIDs =  new int[] { 0, 2, 1, 0, 1, 3, 0, 2, 4 };      int[] loseTeamIDs = new int[] { 1, 3, 2, 4, 3, 5, 5, 4, 5 };      Console.WriteLine("Data: \n");      for (int i = 0; i < winTeamIDs.Length; ++i) {        Console.WriteLine("game: " + i + "   winning team: " +          teamNames[winTeamIDs[i]] + "   losing team: " +          teamNames[loseTeamIDs[i]]);      }      // ===== Define a probabilistic model =======================      Range teamIDsRange = new Range(N).Named("teamsIDRange");      Range gameIDsRange =        new Range(winTeamIDs.Length).Named("gameIDsRange");      double mean = 2000.0;      double sd = 200.0;      double vrnc = sd * sd;            Console.WriteLine("\nDefining Gaussian model mean = " +        mean.ToString("F1") + " and sd = " + sd.ToString("F1"));      VariableArray<double> strengths =        Variable.Array<double>(teamIDsRange).Named("strengths");            strengths[teamIDsRange] =        Variable.GaussianFromMeanAndVariance(mean,        vrnc).ForEach(teamIDsRange);      VariableArray<int> winners =        Variable.Array<int>(gameIDsRange).Named("winners");      VariableArray<int> losers =        Variable.Array<int>(gameIDsRange).Named("losers");      winners.ObservedValue = winTeamIDs;      losers.ObservedValue = loseTeamIDs;      using (Variable.ForEach(gameIDsRange))      {        var ws = strengths[winners[gameIDsRange]];        var ls = strengths[losers[gameIDsRange]];        Variable<double> winnerPerf =      Variable.GaussianFromMeanAndVariance(ws, 400).Named("winPerf");        Variable<double> loserPerf =      Variable.GaussianFromMeanAndVariance(ls, 400).        Named("losePerf");        Variable.ConstrainTrue(winnerPerf > loserPerf);      }      // ===== Infer team strengths using win-loss data ===========      Console.WriteLine("\nInferring strengths from win-loss data");      var iengine = new InferenceEngine();      iengine.Algorithm = new ExpectationPropagation();      iengine.NumberOfIterations = 40;      // iengine.ShowFactorGraph = true;  // needs Graphviz install      Gaussian[] inferredStrengths =        iengine.Infer(strengths);      Console.WriteLine("Inference complete. Inferred strengths: ");      // ===== Show results =======================================      for (int i = 0; i < N; ++i) {        double strength = inferredStrengths[i].GetMean();        Console.WriteLine(teamNames[i] + ": " +          strength.ToString("F1"));      }      Console.WriteLine("\nEnd demo ");      Console.ReadLine();    } // Main  }} // ns

为了创建此演示程序,我启动了 Visual Studio 2017 Community Edition,选择了 .NET Framework 版本 4.7 随附的控制台应用程序模板项目,并将它命名为 InferStrengths。Infer.NET 可以在 .NET Core 应用程序中运行,但我更喜欢在经典 .NET Framework 中运行它。

在模板代码加载后,我右键单击了“解决方案资源管理器”窗口中的 Program.cs 文件,将此文件重命名为“InferStrengthsProgram.cs”,并允许 Visual Studio 自动为我重命名类 Program。

在“解决方案资源管理器”窗口中,我右键单击了项目名称,并选择了“管理 NuGet 包”选项。在“NuGet”窗口中,我选择了“浏览器”选项卡,并搜索了“Infer.NET”。在结果列表中,我选择了“Microsoft.ML.Probabilistic.Compiler”包(版本 0.3.1810.501),并单击了“安装”按钮。Microsoft 计划在某个时间点将 Infer.NET 迁移到 ML.NET 库中,所以如果找不到 Infer.NET 独立包,请在 ML.NET 包中查找。

设置数据

此演示程序设置六支运动队,如下所示:

string[] teamNames = new string[] { "Angels", "Bruins",  "Comets", "Demons", "Eagles", "Flyers" };int N = teamNames.Length;

由于 Infer.NET 主要处理随机变量,因此在许多程序中,所有数据都是严格意义上的数字。纯粹为了提高可读性,本文将运动队名称指定为字符串,而不是整数索引。输赢数据如下:

int[] winTeamIDs =  new int[]  { 0, 2, 1, 0, 1, 3, 0, 2, 4 };int[] loseTeamIDs =  new int[]  { 1, 3, 2, 4, 3, 5, 5, 4, 5 };

此数据表示,在比赛 [0] 中,运动队 0 (Angels) 击败运动队 1 (Bruins)。在比赛 [1] 中,运动队 2 (Comets) 击败运动队 3 (Demons),依此类推一直到比赛 [8]。通过数值编程,使用这样的并行数组往往是比将数据放入类或结构对象更常见的模式。

请注意,此时演示程序仅使用本机 .NET 数据类型。不过,Infer.NET 有自己的类型系统,数据很快就会转换为 Infer.NET 可使用的类型。此演示程序显示源数据:

for (int i = 0; i < winTeamIDs.Length; ++i) {  Console.WriteLine("game: " + i + " winning team: " +    teamNames[winTeamIDs[i]] + " losing team: " +    teamNames[loseTeamIDs[i]]);}

此演示程序中的数据非常少。与一些通常需要非常大量的定型数据才能发挥作用的机器学习技术(如神经网络或强化学习)相比,能够处理有限数据是概率性编程的强项。

定义概率性模型

此演示程序使用以下语句准备高斯模型:

Range teamIDsRange = new Range(N).Named("teamsIDRange");Range gameIDsRange =  new Range(winTeamIDs.Length).Named("gameIDsRange");double mean = 2000.0;double sd = 200.0;double vrnc = sd * sd;

在 Infer.NET 中,Range 对象的概念是很重要的。与通常使用 for 循环或 foreach 循环进行显式循环访问的标准过程式编程不同,在 Infer.NET 中,更常见的做法是通过 Range 对象应用元操作。这种编码范型可能有点难以习惯。

使用链接到构造函数调用的 Named 方法,几乎可以为所有 Infer.NET 对象提供可选的字符串名称。对象字符串名称不需要与对象标识符名称匹配,但最好保持一致。不久就会发现,Named 方法很实用,因为 Infer.NET 有一种内置方式可用来显示模型的计算图,此图使用字符串名称(若有),而不是库生成的名称(如 vdouble23)。

要推断的运动队实力是使用下面两个语句进行设置:

VariableArray<double> strengths =  Variable.Array<double>(teamIDsRange).Named("strengths");strengths[teamIDsRange] =  Variable.GaussianFromMeanAndVariance(mean,  vrnc).ForEach(teamIDsRange);

第一个语句将一个对象设置为特殊的 VariableArray 类型,其中包含六个随机 Variable 对象。第二个语句将每个随机变量初始化为,平均值 = 2000 且方差 = 4000(相当于标准偏差 = 200)的高斯分布。可以设置包含各个 Variable 对象的数组,如下所示:

Variable<double>[] arr = new Variable<double>[6];for (int i = 0; i < 6; ++i)  arr[i] = Variable.GaussianFromMeanAndVariance(2000, 4000);

不过,为了提升性能,Infer.NET 文档建议使用 VariableArray 方法。接下来,创建 int 类型的随机变量,用于保留输赢运动队的索引,再将本机 .NET int 数据转换为 Infer.NET 对象:

VariableArray<int> winners =  Variable.Array<int>(gameIDsRange).Named("winners");VariableArray<int> losers =  Variable.Array<int>(gameIDsRange).Named("losers");winners.ObservedValue = winTeamIDs;losers.ObservedValue = loseTeamIDs;

在这种情况下,随机变量不遵循概率分布;而基本上只是普通的整数值。此时,使用 ObservedValue 属性来设置固定值。

定义概率性模型特征的关键语句如下:

using (Variable.ForEach(gameIDsRange)) {  var ws = strengths[winners[gameIDsRange]];  var ls = strengths[losers[gameIDsRange]];  Variable<double> winnerPerf =    Variable.GaussianFromMeanAndVariance(ws,    400.0).Named("winPerf");  Variable<double> loserPerf =    Variable.GaussianFromMeanAndVariance(ls,    400.0).Named("losePerf");  Variable.ConstrainTrue(winnerPerf > loserPerf);}

这段代码相当微妙。由于代码位于 Variable.ForEach 块内,因此操作以元方式应用到每支运动队和每个比赛结果。将与每场比赛的输赢运动队的表现相对应的随机变量定义为,以运动队的实力为依据,但这些变量有高斯分布(方差 = 400)的噪声分量。可以认为,在推断运动队的实力时,一支运动队可能会略微表现不佳,而对手可能会胜算略大。噪声方差值 400 必须通过反复试验才能确定。

ConstrainTrue 语句很关键,它添加了允许推理引擎计算每支运动队实力的逻辑。推理引擎使用复杂算法,以对六支运动队中的每支运动队检查不同的平均值和方差,再根据假定的平均值和方差确定观察到的输赢结果的可能性。推理算法寻找与观察数据最匹配的六个平均值和方差。聪明!

推断运动队实力

定义了概率性模型后,便会使用以下语句创建推理引擎:

var iengine = new InferenceEngine();iengine.Algorithm = new ExpectationPropagation();iengine.NumberOfIterations = 40;// iengine.ShowFactorGraph = true;

Infer.NET 支持三种不同的算法:期望传播、变分消息传递和吉布斯采样。不同的算法适用于不同类型的概率性模型。对于此演示程序模型,仅期望传播有效。期望传播是 Infer.NET 特有的算法,最大限度地减少 Kullback-Liebler 散度指标,以近似计算一组观察数据的概率分布。NumberOfIterations 属性的值必须通过反复试验才能确定。

Infer.NET 的一个有趣特性是,可以自动生成模型计算图的可视化表示形式,如图 3 中与此演示程序对应的可视化表示形式。此功能依赖开放源代码的 Graphviz 程序。如果你将 ShowFactorGraph 设置为 true,且安装了 Graphviz,图便会以 SVG 格式保存在当前工作目录中,并能在浏览器中显示。

图3:计算图的可视化表示形式

创建推理引擎后,可以使用 Infer 方法轻松计算和显示运动队实力:

Gaussian[] inferredStrengths =  iengine.Infer(strengths);for (int i = 0; i < N; ++i) {  double strength = inferredStrengths[i].GetMean();  Console.WriteLine(teamNames[i] + ": " + strength);}

strength 对象传递到 Infer。回想一下,strength 是类型 VariableArray,这是高斯随机变量对象的集合,这些对象与包含输赢数据的输赢运动队 VariableArray 对象相关联。请注意,Infer.NET 模型是松散连接的对象集合,而不是一个顶级对象。对我而言,至少在刚开始接触 Infer.NET 时,我需要一段时间才能适应这个概念。

总结

使用 Infer.NET 进行概率性编程与使用标准过程式语言进行编程截然不同,其中涉及到一条重要的学习曲线。本文中的示例(非严格)基于 Microsoft Research 开发用于 Xbox 安排比赛的 TrueSkill 排名系统。但概率性编程可以应用于除分级和排名之外的许多问题。Infer.NET 有很好的文档,这对起源于研究的代码库来说有点不寻常。若要详细了解概率性编程,我建议学习 bit.ly/2rEr784 上的教程。


Dr.James McCaffrey 供职于华盛顿地区雷蒙德市沃什湾的 Microsoft Research。他从事多个 Microsoft 产品(包括 Internet Explorer 和必应)的相关工作。Dr.可通过 jamccaff@microsoft.com 与 McCaffrey 取得联系。

衷心感谢以下 Microsoft 技术专家对本文的审阅:Chris Lee 和 Ricky Loynd

f分布表完整图a=0.01_使用 Infer.NET 评价竞争对手相关推荐

  1. f分布表完整图a=0.01_建筑电气工程图的一般规定

    建筑工程图的格式与幅面尺寸 1.图纸格式 一张图纸的完整图面是由边框线.图框线.标题栏.会签栏等组成的,其格式如图1-1所示. 图1-1 图纸格式示例 (a)留装订边;(b)不留装订边 2.图纸幅面尺 ...

  2. f分布表完整图a=0.01_自动控制原理2.2.2传递函数零极点,零点如何影响输出响应曲线...

    2.传递函数零点和极点 传递函数分子多项式和分母多项式经因式分解后可写为如下形式: Zi是分子多项式零点,称为传递函数零点,Pj是分母多项式零点,称为传递函数极点.系数K*=b0/a0称为传递函数系数 ...

  3. f分布表完整图a=0.05_MySQL8.0新特性-invisible indexes

    作者 李春·沃趣科技首席架构师 出品 沃趣科技 作者简介: 曾就职于阿里巴巴,全程参与阿里数据架构从Oracle迁移到MySQL过程,参与分布式中间件Cobar设计. | 导语 MySQL 8.0版本 ...

  4. f分布表完整图a=0.05_2019年05月16日,沪深A股股票分析

    纽约华尔街,伦敦金融城,Tier 1投行,我们希望撕掉标签,用数据说话. 欢迎您留言和赞赏,谢谢.一.整体分析 我们选取了沪深A股的股票,利用历史数据对超过60种交易策略进行了预测回测. 下表中列出了 ...

  5. f分布表完整图a=0.01_SQL Server从入门到精通——学习笔记01(数据库基础知识篇)...

    1.1 数据库的概念 数据库(DataBase,DB):是存放数据的仓库,只不过这些数据存在一定的关联,并按一定的格式存放在计算机上. 1.2 数据库管理系统 数据库管理系统(DataBase Man ...

  6. f分布表完整图_【教育统计答疑】如何理解正态分布、均值分布、^2分布、t分布和F分布...

    许多教育统计的初学者都表示这几个分布感到学起来非常吃力,结合最近上课的体会以及答疑的情况,觉得很有必要在这里简单地对这部分内容进行澄清和梳理,以助理解. 首先,"为什么要学习这几个分布&qu ...

  7. f分布表完整图_分布式计算引擎之星——Spark

    点击上方"数风云"关注我们吧 文/李萌 Spark作为分布式计算引擎的一颗璀璨之星,继承了MapReduce分布式并行计算的优点,并改进了MapReduce明显的缺陷.它使用一种面 ...

  8. f分布表完整图_标准正态分布函数数值表怎么查?(加完整分布函数表)

    最近在整理数据时,忽然想到数理统计的其中一种分布,相信作为质量人一定不陌生,我们常常提到数据的分布是否服从正态分布,这是对一组连续数据分布一种描述,还会涉及到如何检验正态分布,对于这个分布的来龙去脉和 ...

  9. 如何利用EXCEL生成任意自由度任意显著因子的F分布表

    1.Excel软件中的FINV()函数.当已知自由度,求某累计概率所对应的F值时,可以使用Excel软件中的FINV()函数.有了此函数,即可省去查F分布表的麻烦.该函数的语法格式为:FINV(Pro ...

最新文章

  1. 浏览新闻oracle的数据结构,oracle 数据结构探索之旅二 [2]
  2. java程序的运行结果依赖操作系统吗_java模拟试卷及答案及解析4
  3. 随机对照试验设计有哪些先天优势?
  4. MySQL数据库中常见的日志文件汇总!
  5. Chrome跨域问题
  6. 《计算机应用基础》模拟试卷三,2015年《计算机应用基础》模拟试题及答案(一)...
  7. C#LeetCode刷题之#628-三个数的最大乘积( Maximum Product of Three Numbers)
  8. 最详尽使用指南:超快上手Jupyter Notebook
  9. MySQL进阶11--DDL数据库定义语言--库创建/修改/删除--表的创建/修改/删除/复制
  10. python字符串转义表
  11. 一条能让 iPhone 崩溃的 app 通知信息
  12. 常用python内置函数_python常用内置函数
  13. UnrealVS插件使用简介(UBT编译,UE4热更新)
  14. 【三维地图】开发攻略 —— 详解“GeoJSON”技术和应用场景
  15. java web play_玩转 Java Web 应用开发:Play 框架
  16. 【毕业设计】基于springboot的个人博客系统
  17. rabbitMq设置多线程并设置线程池消费处理
  18. python pip 安装包失败问题,下载缓慢问题
  19. XManager5 ssh远程登录服务器工具xshell ,服务器资源上传下载工具xftp
  20. ABP VNext学习日记17

热门文章

  1. Java - 读取Excel并转CSV格式
  2. 什么事宏病毒,宏病毒的判断方法 ,宏病毒的防治和清除
  3. c++opencv批量读图转灰度并修改尺寸
  4. 用java构建企业级自动化框架(前序篇)
  5. 5天假期回家看望老妈--2019 充电宝
  6. python能做app吗_python可以做app么
  7. 维纶触摸屏-实现远程监控
  8. 苹果cms vod.html,苹果cms,怎么可以修改vod\seach.html接口,增加搜索时的内置条件?...
  9. 2022年CCF中国软件大会赞助邀请函
  10. 车辆结构——轮胎模型学习