
在飞桨第三期黑客松活动中,湖北大学计算机与信息工程学院的研究生韩凡宇(队长)和王勇森组建了“源力觉醒”小队,为飞桨新增了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分布的差异。