在飞桨第三期黑客松活动中,湖北大学计算机与信息工程学院的研究生韩凡宇(队长)和王勇森组建了“源力觉醒”小队,为飞桨新增了Gumbel API。
详细了解Gumbel分布背后的数学原理以及应用场景;
深刻了解飞桨和业界概率分布设计实现的方法和技巧。
注:“充分学习Gumbel背后的数学原理”这一点很重要,避免在开发时违背定理。印象最深的是在开发之初,我们没有了解到分布的mean如何计算,直接使用了一个不清楚的计算方式,导致在最开始的版本中,mean的计算方式错误。最后,通过查询资料得知标准Gumbel分布的mean为负欧拉常数,于是纠正。
API的名称直接使用Gumbel分布的名称,参数保持Gumbel分布最原生的参数,包括“位置参数loc”以及“尺度参数scale”。预期Gumbel API的形式为:
Gumbel分布类的初始化方法
Gumbel API的功能
数据类型
上述判断实现如下:
if not isinstance(loc, (numbers.Real, framework.Variable)):
(抛出数据类型错误)
if not isinstance(scale, (numbers.Real, framework.Variable)):
(抛出数据类型错误)
if isinstance(loc, numbers.Real):
(转为 paddle 类型的 tensor)
if isinstance(scale, numbers.Real):
(转为 paddle 类型的 tensor)
if loc.shape != scale.shape:
self.loc, self.scale = paddle.broadcast_tensors([loc, scale])
else:
self.loc, self.scale = loc, scale
父类调用
loc + scale * γ
variance:方差
pow( scale, 2 ) * pi * pi / 6
stddev:标准差
sqrt( variance )
cdf(value):累积分布函数
exp(- exp(-(value - loc) / scale))
rsample(shape):重参数化采样
ExpTransform()
AffineTransform(0, -ones_like(scale))
AffineTransform(loc, -scale)
chain = ChainTransform(ExpTransform(), AffineTransform(0, -ones_like(scale)), AffineTransform(loc, -scale))
chain.forward(base_distribute)
参数(loc, scale)的形状,数据类型的判断;
使用基础分布Uniform以及transforms调用父类TransformedDistribution。
def __init__(self, loc, scale):
if not isinstance(loc, (numbers.Real, framework.Variable)):
raise TypeError(
f "Expected type of loc is Real|Variable, but got {type(loc)}")
if not isinstance(scale, (numbers.Real, framework.Variable)):
raise TypeError(
f "Expected type of scale is Real|Variable, but got {type(scale)}"
)
if isinstance(loc, numbers.Real):
loc = paddle.full(shape=(), fill_value=loc)
if isinstance(scale, numbers.Real):
scale = paddle.full(shape=(), fill_value=scale)
if loc.shape != scale. shape:
self.loc, self.scale = paddle.broadcast_tensors([loc, scale])
else:
self.loc, self.scale = loc, scale
finfo = np.finfo(dtype= 'float32')
self.base_dist = paddle.distribution.Uniform(
paddle.full_like( self.loc, float(finfo.tiny)),
paddle.full_like( self.loc, float( 1 - finfo.eps)))
self.transforms = ()
super(Gumbel, self).__init_ _( self.base_dist, self.transforms)
def rsample(self, shape):
exp_trans = paddle.distribution.ExpTransform()
affine_trans_1 = paddle.distribution.AffineTransform(
paddle.full(shape= self.scale.shape,
fill_value= 0,
dtype= self.loc.dtype), -paddle.ones_like( self.scale))
affine_trans_2 = paddle.distribution.AffineTransform(
self.loc, - self.scale)
return affine_trans_2.forward(
exp_trans.inverse(
affine_trans_1.forward(
exp_trans.inverse( self._base.sample(shape)))))
其他属性、方法实现
import paddle
from paddle.distribution.gumbel import Gumbel
loc = paddle.full([ 1], 0.0)
scale = paddle.full([ 1], 1.0)
dist = Gumbel(loc, scale)
使用rsample(shape) / sample(shape)进行随机采样/重参数化采样,需要指定采样的shape。
shape = [ 2]
dist.sample(shape)
# Tensor(shape=[ 2, 1], dtype=float32, place=Place(gpu: 0), stop_gradient=True, [[-0.27544352], [-0.64499271]])
dist.rsample(shape)
# Tensor(shape=[ 2, 1], dtype=float32, place=Place(gpu: 0), stop_gradient=True, [[0.80463481], [0.91893655]])
如何使用GitHub进行代码协作开发;
更加熟悉国内顶尖的飞桨深度学习框架;
深刻掌握了包括Gumbel分布在内的多个数学分布;
锻炼了使用Python开发的能力。
rsample方法中使用多个transforms,操作繁琐;
测试方法时,随机采样和重参数化采样的精度未能降低到1e-3以下。未来将尝试使用其他方式来实现属性方法,从而降低采样精度;
API的__init__方法存在优化的余地,未来可以将rsample中的transforms整合到初始化方法中;
未能实现InverseTransform;
未能将多个参数的验证抽取出来,使用特定的参数验证方法进行验证,比如 _validate_value(value);
未来可以尝试增加kl_divergence计算两个Gumbel分布的差异。