★★★ 本文源自AlStudio社区精品项目,【点击此处】查看更多精品内容 >>>

柯西(Cauchy)分布算子开发

本次开发是由飞桨黑客马拉松中的题目进行进一步详细的分析

任务解析

此任务的目标是在 Paddle 框架中,基于现有概率分布方案进行扩展,新增 Cauchy API, API调用 paddle.distribution.Cauchy
柯西分布是一个数学期望不存在的连续型概率分布。当随机变量X满足它的概率密度函数时,称X服从柯西分布.
柯西分布也叫作柯西一洛伦兹分布,它是以奥古斯丁-路易-柯西与亨德里克-洛伦兹名字命名的连续概率分布,如概述图所示。其概率密度函数为

式中: x 0 {x_0} x0​为定义分布峰值位置的位置参数; λ {\lambda} λ 为最大值一半处的一半宽度的尺度参数。
柯西分布的标注表达式为:

柯西函数的特点:

  1. 数学期望不存在
  2. 方差不存在
  3. 高阶矩均不存在
  4. 柯西分布具有可加性
  5. 柯西分布具有倒数性质

下面是使用python画出的一个柯西分布图:

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
x = np.random.standard_cauchy(size=1000)
plt.hist(x,100,range=(-10,10))
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9T6z2AA-1679471461906)(main_files/main_1_0.png)]

功能目标

本项目的目的是为Paddle贡献柯西分布的算子,因此,需要实现分布算子的常用功能,Cauchy 用于 Cauchy 分布的概率统计与随机采样。API具体包含如下方法:

功能:Creates a Laplace distribution parameterized by loc and scale.

  • mean计算均值;
  • mode 众数;
  • variance计算方差 ;
  • stddev计算标准偏差
  • sample随机采样;
  • rsample 重参数化采样;
  • prob 概率密度;
  • log_prob对数概率密度;
  • entropy 熵计算;
  • cdf 累积分布函数(Cumulative Distribution Function)
  • icdf 逆累积分布函数
  • kl_divergence 返回两个分布之间的kl散度

实现方案

本项目基于paddle.distribution API基类进行开发。 class API 中的具体实现(部分方法已完成开发,故直接使用源代码),该api有两个参数:位置参数self.loc, 尺度参数self.scale

  • mean计算均值;paddle.full(shape, nan, dtype=self.loc.dtype),由于柯西分布没有均值,故返回nan
  • mode 众数 ;self.loc
  • variance计算方差 ;paddle.full(shape, inf, dtype=self.loc.dtype),由于柯西分布没有方差,故返回nan
  • stddev计算标准偏差 (2 ** 0.5) * self.scale
  • sample随机采样; with paddle.no_grad(): return self.rsample(shape)
  • rsample 重参数化采样;
    def rsample(self, sample_shape):
    shape = sample_shape or (1,)
    eps = paddle.to_tensor(numpy.random.standard_cauchy(shape), dtype=self.loc.dtype)
    return self.loc + eps * self.scale
    
  • prob 概率密度;-math.log(math.pi) - self.scale.log() - (1 + ((value - self.loc) / self.scale)**2).log()
  • log_prob对数概率密度;
    loc, scale, value = self._validate_value(value)
    tmp = 1 + ((value - loc) / scale)**2
    tmp = paddle.cast(paddle.to_tensor(tmp), value.dtype)
    scale = paddle.cast(paddle.to_tensor(scale), value.dtype)
    loc = paddle.cast(paddle.to_tensor(loc), value.dtype)
    return -math.log(math.pi) - paddle.log(scale) - paddle.log(tmp)
    
  • entropy 熵计算;math.log(4 * math.pi) + self.scale.log()
  • cdf 累积分布函数(Cumulative Distribution Function)paddle.atan((value - self.loc) / self.scale) / math.pi + 0.5)
  • icdf 逆累积分布函数paddle.tan(math.pi * (value - 0.5)) * self.scale + self.loc
  • 注册KL散度 参照laplace散度注册

同时在paddle/distribution/kl.py 中注册_kl_cauchy_cauchy函数,使用时可直接调用kl_divergence计算cauchy分布之间的kl散度。

算子测试

测试考虑的 case 如下:

根据api类各个方法及特性传参的不同,把单测分成三个部分:测试分布的特性(无需额外参数)、测试分布的概率密度函数(需要传值)以及测试KL散度(需要传入一个实例)。

  1. 测试Cauchy分布的特性
    测试方法:该部分主要测试分布的均值、方差、熵等特征。类TestCauchy继承unittest.TestCase,分别实现方法setUp(初始化),test_mean(mean单测),test_variance(variance单测),test_stddev(stddev单测),test_entropy(entropy单测),test_sample(sample单测)。

    均值、方差、标准差通过Numpy计算相应值,对比Cauchy类中相应property的返回值,若一致即正确

    采样方法除验证其返回的数据类型及数据形状是否合法外,还需证明采样结果符合Cauchy分布。验证策略如下:随机采样30000个Cauchy分布下的样本值,计算采样样本的均值和方差,并比较同分布下scipy.stats.Cauchy返回的均值与方差,检查是否在合理误差范围内;同时通过Kolmogorov-Smirnov test进一步验证采样是否属于Cauchy分布,若计算所得ks值小于0.02,则拒绝不一致假设,两者属于同一分布;

    熵计算通过对比scipy.stats.Cauchy.entropy的值是否与类方法返回值一致验证结果的正确性。

    测试用例:单测需要覆盖单一维度的Cauchy分布和多维度分布情况,因此使用两种初始化参数

    ‘one-dim’: loc=parameterize.xrand((2, )), scale=parameterize.xrand((2, ));
    ‘multi-dim’: loc=parameterize.xrand((5, 5)), scale=parameterize.xrand((5, 5))

  2. 测试Cauchy分布的概率密度函数
    测试方法:该部分主要测试分布各种概率密度函数。类TestCauchyPDF继承unittest.TestCase,分别实现方法setUp(初始化),test_prob(prob单测),test_log_prob(log_prob单测),test_cdf(cdf单测),test_icdf(icdf)。以上分布在scipy.stats.Cauchy中均有实现,因此给定某个输入value,对比相同参数下Cauchy分布的scipy实现以及paddle实现的结果,若误差在容忍度范围内则证明实现正确。

    测试用例:为不失一般性,测试使用多维位置参数和尺度参数初始化Cauchy类,并覆盖int型输入及float型输入。

    ‘value-float’: loc=np.array([0.2, 0.3]), scale=np.array([2, 3]), value=np.array([2., 5.]); * 'value-int': loc=np.array([0.2, 0.3]), scale=np.array([2, 3]), value=np.array([2, 5]);
    ‘value-multi-dim’: loc=np.array([0.2, 0.3]), scale=np.array([2, 3]), value=np.array([[4., 6], [8, 2]])
    3. 测试Cauchy分布之间的KL散度
    测试方法:该部分测试两个Cauchy分布之间的KL散度。类TestCauchyAndCauchyKL继承unittest.TestCase,分别实现setUp(初始化),test_kl_divergence(kl_divergence)。在scipy中scipy.stats.entropy可用来计算两个分布之间的散度。因此对比两个Cauchy分布在paddle.distribution.kl_divergence下和在scipy.stats.Cauchy下计算的散度,若结果在误差范围内,则证明该方法实现正确。

    测试用例:分布1:loc=np.array([0.0]), scale=np.array([1.0]), 分布2: loc=np.array([1.0]), scale=np.array([0.5])

代码实现

本项目参考了Pytorch的柯西分布算子实现方式

import math
from numpy import inf, nan
import numbers
import numpy
import paddle
from paddle.distribution import constraint
from paddle.distribution import Distribution
from paddle.fluid import framework as framework# __all__ = ['Cauchy']class Cauchy(Distribution):r"""Creates a Cauchy distribution parameterized by :attr:`loc` and :attr:`scale`.Mathematical detailsThe probability density function (pdf) is.. math::pdf(x; \mu, \sigma) = \frac{1}{2 * \sigma} * e^{\frac{-|x - \mu|}{\sigma}}In the above equation:* :math:`loc = \mu`: is the location parameter.* :math:`scale = \sigma`: is the scale parameter.Args:loc (scalar|Tensor): The mean of the distribution.scale (scalar|Tensor): The scale of the distribution.Examples:.. code-block:: pythonimport paddlem = paddle.distribution.Cauchy(paddle.to_tensor([0.0]), paddle.to_tensor([1.0]))m.sample()  # Cauchy distributed with loc=0, scale=1# Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,[4.26052284])"""def __init__(self, loc, scale):arg_constraint = {'loc': constraint.real, 'scale': constraint.positive}support = constraint.realif (len(scale.shape) > 0 or len(loc.shape) > 0) and (loc.dtype == scale.dtype):self.loc, self.scale = paddle.broadcast_tensors([loc, scale])else:self.loc, self.scale = loc, scalesuper().__init__(self.loc.shape)@propertydef mean(self):"""Mean of distribution.Returns:Tensor: The mean value."""return paddle.full(shape=(), fill_value=nan, dtype=self.loc.dtype)@propertydef mode(self):"""Mode of distribution.Returns:Tensor: The mode value."""return self.loc@propertydef variance(self):return paddle.full(shape=(), fill_value=inf, dtype=self.loc.dtype)def sample(self, shape=()):r"""Generate samples of the specified shape.Args:shape(tuple[int]): The shape of generated samples.Returns:Tensor: A sample tensor that fits the Laplace distribution.Examples:.. code-block:: pythonimport paddlem = paddle.distribution.Laplace(paddle.to_tensor([0.0]), paddle.to_tensor([1.0]))m.sample()  # Laplace distributed with loc=0, scale=1#Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,[4.26052284])"""if not isinstance(shape, tuple):raise TypeError(f'Expected shape should be tuple[int], but got {type(shape)}')with paddle.no_grad():return self.rsample(shape)def rsample(self, sample_shape):shape = sample_shape or (1,)eps = paddle.to_tensor(numpy.random.standard_cauchy(shape), dtype=self.loc.dtype)return self.loc + eps * self.scaledef log_prob(self, value):loc, scale, value = self._validate_value(value)tmp = 1 + ((value - loc) / scale)**2tmp = paddle.cast(paddle.to_tensor(tmp), value.dtype)scale = paddle.cast(paddle.to_tensor(scale), value.dtype)loc = paddle.cast(paddle.to_tensor(loc), value.dtype)return -math.log(math.pi) - paddle.log(scale) - paddle.log(tmp)def cdf(self, value):loc, scale, value = self._validate_value(value)return paddle.atan((value - loc) / scale) / math.pi + 0.5def icdf(self, value):# return paddle.tan(math.pi * (value - 0.5)) * self.scale + self.locreturn paddle.full(shape=(), fill_value=nan, dtype=self.loc.dtype)# np.tan(np.pi*q-np.pi/2.0)def entropy(self):return math.log(4 * math.pi) + self.scale.log()def _validate_value(self, value):"""Argument dimension check for distribution methods such as `log_prob`,`cdf` and `icdf`.Args:value (Tensor|Scalar): The input value, which can be a scalar or a tensor.Returns:loc, scale, value: The broadcasted loc, scale and value, with the same dimension and data type."""if isinstance(value, numbers.Real):value = paddle.full(shape=(), fill_value=value)if value.dtype != self.loc.dtype:value = paddle.cast(value, self.loc.dtype)if self.scale.dtype != self.loc.dtype:scale = paddle.cast(self.scale, self.loc.dtype)else:scale = self.locif (len(self.scale.shape) > 0or len(self.loc.shape) > 0or len(value.shape) > 0):loc, scale, value = paddle.broadcast_tensors([self.loc, scale, value])else:loc, scale = self.loc, self.scalereturn loc, scale, value

测试代码(单测)

测试代码必须放置在/mnt/cfs/algorithm/yunji.feng/code/Paddle/python/paddle/fluid/tests/unittests/distribution目录下,它会引用在paddle源码中实现的parameterize文件,并使用unittest模块完成整个测试任务。
Unittest是Python内部自带的一个单元测试的模块,具备完整的测试结构,支持自动化测试的执行,对测试用例集进行组织,并且提供了丰富的断言方法,最后生成测试报告。Unittest框架的初衷是用于单元测试,但也不限于此,在实际工作中,由于它强大的功能,提供的完整的测试流程,我们往往将其用于自动化测试的各个方面。
np.testing.assert_allclosenumpy的一个测试功能,能够对比两个函数的输出是否一致,其输入参数为:

参数 类型 说明
actual array_like 实际数组
desired array_like 期望数组
rtol float 相对 tolerance
atol float 绝对 tolerance
equal_nan bool True 时认为 NaN 值相等
err_msg str 断言失败时的错误信息
verbose bool 在错误信息中是否附加冲突值

通过np.testing.assert_allclose,能够对本项目实现的Cauchy算子运算的正确性进行检验,这里使用了scipyCauchy函数作为期望值。

import unittest
import numpy as np
import paddle
import scipy.statsimport config
import parameterizeSEED = 2023@parameterize.place(config.DEVICES)
@parameterize.parameterize_cls((parameterize.TEST_CASE_NAME, 'loc', 'scale'), [('one-dim', parameterize.xrand((2, )),\parameterize.xrand((2, ))),('multi-dim', parameterize.xrand((5, 5)),\parameterize.xrand((5, 5))),])
class TestCauchy(unittest.TestCase):def setUp(self):self._dist = paddle.distribution.Cauchy(loc=paddle.to_tensor(self.loc),scale=paddle.to_tensor(\self.scale))def test_entropy(self):entropy = self._dist.entropy()self.assertEqual(entropy.numpy().dtype, self.scale.dtype)def _np_entropy(self):return scipy.stats.cauchy.entropy(loc=self.loc, scale=self.scale)def _kstest(self, loc, scale, samples):# Uses the Kolmogorov-Smirnov test for goodness of fit.ks, p_value = scipy.stats.kstest(samples,scipy.stats.cauchy(loc, scale=scale).cdf)return ks < 0.02@parameterize.place(config.DEVICES)
@parameterize.parameterize_cls((parameterize.TEST_CASE_NAME, 'loc', 'scale', 'value'),[('value-float', np.array([0.2, 0.3]),\np.array([2, 3]), np.array([2., 5.])),('value-int', np.array([0.2, 0.3]),\np.array([2, 3]), np.array([2, 5])),('value-multi-dim', np.array([0.2, 0.3]), np.array([2, 3]),\np.array([[4., 6], [8, 2]])),])
class TestCauchyPDF(unittest.TestCase):def setUp(self):self._dist = paddle.distribution.Cauchy(loc=paddle.to_tensor(self.loc),scale=paddle.to_tensor(\self.scale))def test_prob(self):np.testing.assert_allclose(self._dist.prob(paddle.to_tensor(self.value)),scipy.stats.cauchy.pdf(self.value, self.loc, self.scale),rtol=config.RTOL.get(str(self.loc.dtype)),atol=config.ATOL.get(str(self.loc.dtype)))def test_log_prob(self):np.testing.assert_allclose(self._dist.log_prob(paddle.to_tensor(self.value)),scipy.stats.cauchy.logpdf(self.value, self.loc, self.scale),rtol=config.RTOL.get(str(self.loc.dtype)),atol=config.ATOL.get(str(self.loc.dtype)))def test_cdf(self):np.testing.assert_allclose(self._dist.cdf(paddle.to_tensor(self.value)),scipy.stats.cauchy.cdf(self.value, self.loc, self.scale),rtol=config.RTOL.get(str(self.loc.dtype)),atol=config.ATOL.get(str(self.loc.dtype)))def test_icdf(self):np.testing.assert_allclose(self._dist.icdf(paddle.to_tensor(self.value)),scipy.stats.cauchy.ppf(self.value, self.loc, self.scale),rtol=config.RTOL.get(str(self.loc.dtype)),atol=config.ATOL.get(str(self.loc.dtype)))if __name__ == '__main__':
,atol=config.ATOL.get(str(self.loc.dtype)))if __name__ == '__main__':unittest.main()

总结

在实现过程中,学习Pytorch的实现方式能够很快地写出柯西分布的核心代码,但是在测试过程中会出现各种问题,比如数据类型不同、Nan返回错误…等。

本项目是笔者第一次接触Paddle框架的底层实现方式,比想象中要容易很多,但是算子的测试是十分严谨和复杂的,中途遇到的大多数问题在网上都没有解答,只能够根据算法的实现方式去排除bug,进一步加深了对Paddle框架的理解

本项目是AI特训营的项目之一,很开心能够在这样的活动中学到新的东西,Paddle YYDS!!

【AI特训营】:柯西分布 Paddle API实现相关推荐

  1. AI实训营笔记【6.11更新至笔记7】

    5.31开营 需要交至少3次作业(共5次) 算法库 MMOCR 文字检测库 提取结构化信息 MMDetection3D MMRotate 旋转目标检测 图像分割算法库 图像分割是识别提取的 图像分类+ ...

  2. 【AI达人特训营】强化学习优化离散制造系统

    [AI达人特训营]强化学习优化离散制造系统 0.简介 在制造系统中,产线工位设置.工艺流程的组织直接影响车间生产效率.产量及成本,关系到企业盈利情况."中国制造2025"国家行动纲 ...

  3. 国际顶级学界和工业界大咖云集、AIoT 实训营,你不可错过的嵌入式 AI 盛会!...

    随着物联网和人工智能技术的飞速发展与相互渗透,万物智联的新赛道已经开始显现.据中商产业研究院<2016-2021年中国物联网产业市场研究报告>显示,预计到2020年,中国物联网的整体规模将 ...

  4. 【Go入门】Socks5代理服务器实现 翻译api调用 | 青训营笔记

    这是我参与「第三届青训营 -后端场」笔记创作活动的的第一篇笔记. [课程一]Go基础 主要内容 第一节课主要介绍了go的语法基础,包括变量.循环分支.数组切片.函数方法.错误处理.字符串.格式化处理. ...

  5. 笔记 | 百度飞浆AI达人创造营:深度学习模型训练和关键参数调优详解

    笔记 | 百度飞浆AI达人创造营:深度学习模型训练和关键参数调优详解 针对特定场景任务从模型选择.模型训练.超参优化.效果展示这四个方面进行模型开发. 一.模型选择 从任务类型出发,选择最合适的模型. ...

  6. 【AI达人创造营第二期】基于PaddleClas的新冠肺炎CT影像的分类

    转自AI Studio,原文链接:[AI达人创造营第二期]基于PaddleClas的新冠肺炎CT影像的分类 - 飞桨AI Studio 一.项目背景 新近爆发的2019新型冠状病毒(SARS-CoV- ...

  7. 【组队学习】【34期】百度飞桨AI达人创造营

    百度飞桨AI达人创造营 航路开辟者:百度飞桨 领航员:六一 航海士:阿水.颜鑫.宋泽山.刘洋.张文恺 基本信息 内容属性:合作课程 练习平台:https://aistudio.baidu.com/ai ...

  8. 亚马逊首席科学家李沐「实训营」国内独家直播,马上报名 !

    开学了,别人家的学校都开始人工智能专业的学习之旅了,你呢? 近年来,国内外顶尖科技企业的 AI 人才抢夺战愈演愈烈.华为开出200万年薪吸引 AI 人才,今年又有 35 所高校新增人工智能本科专业,众 ...

  9. 抓住人工智能红利,百度AI达人养成营带你速成深度学习

    未来几年,什么行业是最值得期待的"风口"?又是学什么专业才可以薪资爆表? 国际权威调研机构IDC发布的一份报告显示,2024年中国人工智能市场规模将达到127.5亿美金.随之而来的 ...

最新文章

  1. 1.15 总结-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  2. 一文看懂集群、分布式与负载均衡的关系
  3. ios-团购代码的重构
  4. 网易2016 实习研发工程师 [编程题]寻找第K大 and leetcode 215. Kth Largest Element in an Array...
  5. 54.Linux/Unix 系统编程手册(下) -- POSIX 共享内存
  6. 使用JGit报错:Algorithm negotiation fail 的解决方案
  7. 另类搞笑:自我指涉例句不完全收集
  8. 草稿 22.10.10 !!!!!! YUYU
  9. CentOS7 防火墙开放端口配置
  10. Python哈希表的例子:dict、set
  11. 指纹识别综述(5): 分类与检索
  12. Partial Class
  13. 以太网数据包速率计算方法
  14. 边缘计算招聘 :边缘不再「边缘」,计算无处不在
  15. Android 打开手机淘宝,并自动识别淘口令,弹出商品信息
  16. 以太坊源码学习(一) 正本清源
  17. 三周年之后 网易游戏和阴阳师都跌落神坛了?
  18. 树袋熊无线网络打印服务器,树袋熊
  19. Arduino与Proteus仿真实例-74HC148优先编码器驱动仿真
  20. C51单片机 波特率选择 和 串口的工作方式

热门文章

  1. Nuxt.js(Vue SSR)项目配置以及开发细节
  2. java多级审批流程_[SharePoint 工作流] 如何设计一个通用的多级多审核工作流程(三):工作流开发...
  3. git bash here将本地代码更新到远程仓库
  4. Angular CLI ng常用命令整理
  5. 文字图片转换成html,将图片转换成HTML格式的文字图程序源代码
  6. 测试开发进阶——APP性能监测工具——itest
  7. 洛谷 P1862 输油管道问题
  8. C艹开源可读项目整理
  9. 一个暴风雨的夜晚——一个普通而又伟大老师的一天
  10. Spring系列之Spring Web MVC-20