网易首页 > 网易号 > 正文 申请入驻

用70行Python编写一个概率编程语言

0
分享至

作者 | Andrea Cognolato 译者 | 弯月

出品 | CSDN(ID:CSDNnews)

简介

在这篇文章中,我将介绍概率编程语言(Probabilistic Programming Languages,简称PPL)的工作原理,并逐步演示如何用Python构建一个简单的概率编程语言。

本文主要面向的读者是统计学家、AI研究员和好奇的程序员,相信大家都熟悉 PPL 和贝叶斯统计,并掌握了基本的 Python知识。

我们将要构建的API如下:


mu =LatentVariable("mu",Normal, [0.0, 5.0])y_bar =ObservedVariable("y_bar", Normal, [mu, 1.0],observed=3.0)
evaluate_log_density(y_bar,{"mu": 4.0})

前两行定义了统计模型:

最后一行求在条件下,在 μ = 4 处该模型定义的(未正规化的)概率分布。

希望本文能让读者理解 PPL 的工作原理,并了解如何用 Python 实现一门嵌入式领域专用语言(Embedded Domain-Specific Languages,简称EDSL)。

相关研究

据我所知,目前尚没有使用Python的PPL实现。

  • 《TheDesign and Implementation of Probabilistic Programming Languages》一书的重点是编程语言理论,需要读者熟悉 continuation-passing style、协程,而且采用了JavaScript 作为实现语言。

  • 《Anatomyof a Probabilistic Programming Framework》一文(https://www.georgeho.org/prob-prog-frameworks/)是很不错的高层概要,但并没有涉及具体实现细节和代码示例。

  • Junpeng Lao的演讲(https://www.youtube.com/watch?v=WHoS1ETYFrw&feature=youtu.be)和 PyMC3的开发者指南(https://docs.pymc.io/en/v3/developer_guide.html)描述了 PyMC 的具体实现细节,但想根据这些内容实现一个 PPL 并不容易。

实现
高层表示

我们使用下述模型作为基本的参考:

这两个表达式定义了一个联合概率分布,对应的概率密度函数(Probability Density Function,简称PDF)为:

可以用两种形式的图来表示这个表达式(和模型):一种是图模型,一种是有向因子图。

左:用概率图模型(PGM)表示的模型。右:用有向因子图表示的模型。

虽然在各类文档中PGM方式更常见,但我认为LFG对于实现PPL的人来说更有用。这个图揭示了几个重要方面:

  • 我们需要一种方式来表达两种变量:

  • 一种是观测到的值(,灰色背景)

  • 一种是无法观测到的隐变量(μ,白色背景)

  • 我们需要处理常数,以及每个变量的分布。

  • 最后,我们需要一种方法将观测值、隐变量和常数连接起来。

分布

在文本中,分布指的是一个类,它带有一个函数,可以求在某个点上的对数概率密度。log_density 函数接受一个 float 类型的参数,表示分布支持的一个点;一个 List[float],表示分布的参数;返回一个 float,是该分布在该点上的对数PDF。要实现新的分布,只需从 Distribution 虚类集成即可。暂时不支持向量或矩阵分布。

我们使用 SciPy 实现一个 Normal 分布,param[0] 是均值,param[1] 是标准差。


from scipy.stats import norm
classDistribution:@staticmethoddef log_density(point, params):raise NotImplementedError("Mustbe implemented by a subclass")
classNormal(Distribution):@staticmethoddef log_density(point, params):returnfloat(norm.logpdf(point, params[0], params[1]))

变量和DAG

现在我们来看看变量。这些变量包含三个方面:它们有关联的分布,可以为隐变量或观测变量,它们之间互相连接(即变量可以有子变量)。

dist_class字段就是变量关联的分布 Distribution。在需要求完整的对数密度时,我们会使用该字段访问变量的分布的log_density方法。

区分隐变量和观测变量的方式是使用 LatentVariable 和 ObservedVariable 类。观测变量有一个observed 字段,其中包含了观测的值。由于隐变量在建模阶段没有值,我们必须在运行时给其复制,才能求出全部的对数分布。要在运行时给隐变量复制,我们需要用唯一的字符串 name 来区分它们。

最后,我们可以将一个变量的分布的参数设置为变量或常量。在本例中,的平均值是 μ,一个正太随机变量,而它的标准差是常数 1。为了表示它,我们使用 dist_args 属性。mypy 中的 dist_args 的签名为 dist_args: Union[float, LatentVariable, ObservedVariable]。这就是说,一个隐变量或观测变量可以有“参数”,参数本身也可以是常量的隐变量或观测变量,这就创建了一个有向无环图(DAG)。


classLatentVariable:def __init__(self, name, dist_class, dist_args):self.name = nameself.dist_class = dist_classself.dist_args = dist_args

classObservedVariable:def __init__(self, name, dist_class, dist_args, observed):self.name = nameself.dist_class = dist_classself.dist_args = dist_argsself.observed = observed

可视化该DAG,就会注意到它和隐因子图之间的一个重要区别:箭头方向是相反的。其原因是我们的建模API中的变量指定方式。而且,似乎将观测变量作为根节点,能更好地表示计算联合对数密度的过程。

左:将模型表示为有向因子图。右:内存中的DAG表示。

为了进一步说明,我们来看看模型中的 dist_args 是什么样子:


mu =LatentVariable("mu",Normal, [0.0, 5.0])y_bar =ObservedVariable("y_bar", Normal, [mu, 1.0],observed=5.0)
print(mu)#=> <__main__.LatentVariable object at 0x7f14f96719a0>print(mu.dist_args)#=> [0.0, 5.0]print(y_bar)#=> <__main__.ObservedVariable object at 0x7f14f9671940>print(y_bar.dist_args)# => [<__main__.LatentVariable object at 0x7f14f96719a0>,1.0]

求出对数密度

我们距离目标不远了,现在还缺一种通过DAG计算联合对数密度的方法。我们需要遍历DAG,将每个变量的对数密度加在一起。对数密度加法相当于密度的乘法,但加法在数值上更稳定。

遍历DAG需要使用一个递归算法,叫做深度优先搜索。collect_variables函数会访问所有变量一次,将所有非float变量收集到一个列表中。然后算法从根节点开始,递归地遍历收集到的每个变量的所有dist_args。


defevaluate_log_density(variable, latent_values):visited = set()variables = []
def collect_variables(variable):if isinstance(variable, float):return
visited.add(variable)variables.append(variable)
for arg in variable.dist_args:if arg not in visited:collect_variables(arg)
collect_variables(variable)

对于每个变量,我们需要获取它的每个参数的数值,然后用它求出分布的对数密度。由于float参数已经是数值了,而LatentVariables根据求值所在的位置不同而有不同的值。为了指定隐变量的值,我们需要传递一个包含了从变量名到数值的映射的字典,名为latent_values。注意ObservedVariables不能是参数,只能是根节点。

注意:

  • dist_args可以是float常数,或LatentVariables。

  • dist_params都是float,或者是常数,或者是通过latent_values在运行时(即计算对数密度时)赋给隐变量的值。

最后,我们有了从参数中提取出的分布参数,就可以更新整体的对数密度了。LatentVariable需要在latent_values指定的点上计算对数密度,而ObservedValues会在observed指定的点上计算对数密度。


log_density = 0.0for variable in variables:dist_params = []for dist_arg in variable.dist_args:if isinstance(dist_arg, float):dist_params.append(dist_arg)if isinstance(dist_arg, LatentVariable):dist_params.append(latent_values[dist_arg.name])
if isinstance(variable, LatentVariable):log_density +=variable.dist_class.log_density(latent_values[variable.name],dist_params)if isinstance(variable, ObservedVariable):log_density +=variable.dist_class.log_density(variable.observed, dist_params)
returnlog_density

我们来检查一下整体的对数概率是否与期待的结果一致:


mu =LatentVariable("mu",Normal, [0.0, 5.0])y_bar =ObservedVariable("y_bar", Normal, [mu, 1.0],observed=5.0)
latent_values = {"mu": 4.0}print(evaluate_log_density(y_bar, latent_values))#=> -4.267314978843446print(norm.logpdf(latent_values["mu"], 0.0, 5.0)+ norm.logpdf(5.0, latent_values["mu"], 1.0))# => -4.267314978843446

结论和改进

分布、变量DAG和对数密度计算是概率编程语言的组成部分。变量可以是隐变量或观测变量,在计算对数密度时,每个变量必须单独处理。我们用Python实现了这些概念,从而实现了简单但强大的PPL。

下一步是增加对张量和随机变量变换的支持,从而支持更有用的模型,如线性回归、层次/混合效果模型等。另一个有用的特性是构建一个API用于先验预测采样。最后,我们可以不在Python中进行计算,而是使用像Theano/Aesara、Jax或TensorFlow之类的计算图框架,可以极大地改善性能。计算图还可以通过反式自动微分的方式,利用对数密度计算梯度,这可以用于汉密尔顿·蒙特卡洛等更高级的采样器上。

篇外:后验网格近似

下面,我们讨论一下对数密度的用途。其用途之一就是找到后验分布的模,即找到参数的最有可能的值。

本例中,观测样本的平均值是1.5,在正态零均值先验的条件下,它会向 0 移动一点。这就是说,最大后验估计(Maximum A Posteriori estimate)大约是1.4。


import numpy as npimport pandas as pdimport altair as altfrom smolppl import Normal, LatentVariable, ObservedVariable, evaluate_log_density#Define model#Weakly informative mean priormu =LatentVariable("mu",Normal, [0.0, 5.0])#Observation model. I make some observations y_1, y_2, ..., y_n and compute the#sample mean y_bar. It is given that the sample mean has standard deviation 1.y_bar =ObservedVariable("y_bar", Normal, [mu, 1.0],observed=1.5)#Grid approximation for the posterior#Since the prior has mean 0, and the observations have some uncertainty, I#expect the mode to be a bit smaller than 1.5. Something like 1.4grid =np.linspace(-4, 4, 20)evaluations = [evaluate_log_density(y_bar, {"mu": mu}) for mu in grid]#Plottingdata =pd.DataFrame({"grid":grid, "evaluations": evaluations})chart =alt.Chart(data).mark_line(point=True).encode( x=alt.X('grid', axis=alt.Axis(title="mu")), y=alt.Y('evaluations', axis=alt.Axis(title="logdensity"))).interactive().configure_axis( labelFontSize=16, titleFontSize=16)chart

https://mrandri19.github.io/2022/01/12/a-PPL-in-70-lines-of-python.html

《》正式上市,50余位技术专家共同创作,云原生和数字化的开发者们的一本技术精选图书。内容既有发展趋势及方法论结构,华为、阿里、字节跳动、网易、快手、微软、亚马逊、英特尔、西门子、施耐德等30多家知名公司云原生和数字化一手实战经验!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
“姜萍连题目都看不懂”,北大硕士赵斌500万对赌,称愿承担后果

“姜萍连题目都看不懂”,北大硕士赵斌500万对赌,称愿承担后果

妍妍教育日记
2024-06-19 15:56:18
男子误机花6000从天津打车到西安:出租车司机路上一直问,“兄弟你确定?”

男子误机花6000从天津打车到西安:出租车司机路上一直问,“兄弟你确定?”

天津族
2024-06-20 07:39:06
要大结局了?仁爱礁菲舰出现6米大裂缝,坐滩人员已中断补给25天

要大结局了?仁爱礁菲舰出现6米大裂缝,坐滩人员已中断补给25天

胖福的小木屋
2024-06-17 23:57:30
外媒污蔑中国在英军帽徽中装跟踪器,知名大V:幸亏我们不和傻子生活在一个国家!

外媒污蔑中国在英军帽徽中装跟踪器,知名大V:幸亏我们不和傻子生活在一个国家!

不掉线电波
2024-06-20 12:01:30
永远不要做压死骆驼的最后一根稻草

永远不要做压死骆驼的最后一根稻草

洞见
2024-06-18 21:48:38
社会上流行着“不欠祖国只欠父母”的思想,非常可怕

社会上流行着“不欠祖国只欠父母”的思想,非常可怕

雪莉故事汇
2024-06-18 08:56:23
抗日神剧八路军的伙食,不是海鲜就是法国菜,主打的就是一个上流

抗日神剧八路军的伙食,不是海鲜就是法国菜,主打的就是一个上流

附允历史观
2024-06-19 16:45:10
两性疑问:为什么男生更喜欢从后面来

两性疑问:为什么男生更喜欢从后面来

坟头长草
2024-05-30 16:33:38
黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

黄一鸣“杀疯了”!直播间卖大葱养孩子,王思聪被整得完全没脾气

萌神木木
2024-06-18 21:18:32
布林克INS发文:永远不会想到会发生在自己身上 但这会让我更强大

布林克INS发文:永远不会想到会发生在自己身上 但这会让我更强大

直播吧
2024-06-20 07:57:09
解放战争中,如果国民党获得胜利,今天的中国会是什么样

解放战争中,如果国民党获得胜利,今天的中国会是什么样

史诗长歌
2024-05-13 13:34:32
上海这一夜,由王中磊牵头的明星聚会,将华谊的落魄展现淋漓尽致

上海这一夜,由王中磊牵头的明星聚会,将华谊的落魄展现淋漓尽致

先人后记
2024-06-19 00:01:03
央国企开始疯狂卖资产!

央国企开始疯狂卖资产!

樱桃大房子
2024-06-20 21:46:53
50岁富婆夜间约会帅哥,凌晨对方哀求“歇一会吧”,竟还心怀叵胎

50岁富婆夜间约会帅哥,凌晨对方哀求“歇一会吧”,竟还心怀叵胎

四象八卦
2024-06-20 16:42:28
著名女优玩偶姐姐HongKongDoll,被爆料真实面目?

著名女优玩偶姐姐HongKongDoll,被爆料真实面目?

吃瓜党二号头目
2024-06-13 10:15:52
被保险人“呼吸心跳骤停”死亡,保险公司以“猝死”拒赔引争议

被保险人“呼吸心跳骤停”死亡,保险公司以“猝死”拒赔引争议

澎湃新闻
2024-06-20 19:56:35
1.8亿全是水分?皇马巨星被狠批:眼神防守,一碰就倒,全场0射门

1.8亿全是水分?皇马巨星被狠批:眼神防守,一碰就倒,全场0射门

我的护球最独特
2024-06-21 02:37:24
曝45岁伏明霞离婚,净身出户原因揭晓,71岁百亿丈夫只说6个字

曝45岁伏明霞离婚,净身出户原因揭晓,71岁百亿丈夫只说6个字

深度知局
2024-05-20 19:25:53
尴尬,国际乒联向王曼昱发来奥运单打邀请,但女单为孙颖莎、陈梦

尴尬,国际乒联向王曼昱发来奥运单打邀请,但女单为孙颖莎、陈梦

尘语者
2024-06-19 21:45:38
援手已到,中方90艘舰船就位,南海反包围开始,菲船后撤32海里

援手已到,中方90艘舰船就位,南海反包围开始,菲船后撤32海里

红心说娱乐
2024-06-19 18:52:43
2024-06-21 05:18:44
CSDN
CSDN
成就一亿技术人
24735文章数 241822关注度
往期回顾 全部

科技要闻

小米SU7流量泼天,富贵却被蔚来接住了

头条要闻

欧洲杯:凯恩破门 英格兰1-1丹麦

头条要闻

欧洲杯:凯恩破门 英格兰1-1丹麦

体育要闻

千夫所指的关系户 成了拯救葡萄牙的英雄

娱乐要闻

叶舒华参加柯震东生日聚会,五毒俱全

财经要闻

楼市新“王炸”!释放何信号?

汽车要闻

售价11.79-14.39万元 新一代哈弗H6正式上市

态度原创

本地
健康
时尚
房产
公开课

本地新闻

2024·合肥印象|用崭新视角对话城市发展

晚餐不吃or吃七分饱,哪种更减肥?

当男人不耍帅时,就是最帅的时候(穿衣篇)

房产要闻

海棠湾!一所重量级国际学校真的来了!

公开课

近视只是视力差?小心并发症

无障碍浏览 进入关怀版