首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >表示Haskell中的计算图

表示Haskell中的计算图
EN

Stack Overflow用户
提问于 2020-08-06 08:23:02
回答 1查看 310关注 0票数 2

我试着用Haskell写一个简单的自动区分包。

在Haskell中表示类型安全(有向)计算图的有效方法是什么?我知道广告包为此使用了“data”方法,但我对它不太熟悉。有人能给我提供一些见解吗?谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-08-06 12:43:50

正如Ness的评论所指出的,AD的正确抽象是一个类别,而不是一个图表。不幸的是,标准的班级并没有真正做到这一点,因为它需要任何Haskell类型之间的箭头,但是在光滑流形之间的区分才有意义。大多数库不知道流形,并将其进一步限制在欧氏向量空间(它们表示为“向量”或“张量”,即数组)。没有一个令人信服的理由来限制--任何仿射空间都可以用于前向模式AD;对于反向模式,还需要一个对偶空间的概念来区分向量。

代码语言:javascript
运行
复制
data FwAD x y = FwAD (x -> (y, Diff x -> Diff y))
data RvAD x y = RvAD (x -> (y, DualVector (Diff y) -> DualVector (Diff x)))

其中Diff x -> Diff y函数必须是线性函数。(您可以使用这类函数的专用箭头类型,也可以只使用碰巧是线性的(->)函数。)在反向模式下唯一不同的地方是,这个线性映射的伴随被表示,而不是映射本身。(在实值矩阵实现中,线性映射是雅可比矩阵,伴随版本是它的转置,但不要使用矩阵,它们在这方面效率很低。)

很干净,对吧?人们一直在谈论的那些图/遍历/变异/向后传递的无稽之谈并不是真正需要的。(详细说明请参见科诺氏纸。)

因此,要使它在Haskell中有用,您需要实现类别组合器。这正是我写受限-类别包的目的。下面是您所需的大纲实例化:

代码语言:javascript
运行
复制
import qualified Prelude as Hask
import Control.Category.Constrained.Prelude
import Control.Arrow.Constrained
import Data.AffineSpace
import Data.AdditiveGroup
import Data.VectorSpace

instance Category FwAD where
  type Object FwAD a
     = (AffineSpace a, VectorSpace (Diff a), Scalar (Diff a) ~ Double)
  id = FwAD $ \x -> (x, id)
  FwAD f . FwAD g = FwAD $ \x -> case g x of
     (gx, dg) -> case f gx of
       (fgx, df) -> (fgx, df . dg)

instance Cartesian FwAD where
  ...
instance Morphism FwAD where
  ...
instance PreArrow FwAD where
  ...
instance WellPointed FwAD where
  ...

这些实例都很简单,而且几乎毫不含糊,让编译器消息来指导您(类型化的漏洞_非常有用)。基本上,每当需要范围内的类型的变量时,就使用它;当需要一个不在作用域中的向量空间类型的变量时,使用zeroV

到那时,您将真正拥有所有基本的可微函数工具,但是要真正定义这些函数,您需要使用大量的.&&&***组合器和硬编码的数字基元,这些都是非常规的,也是相当混乱的。为了避免这种情况,您可以使用代理值:值,这些值基本上假装是简单的数字变量,但实际上包含来自某个固定域类型的整个类别箭头。(这基本上就是“建立一个图表”的一部分。)您可以简单地使用提供的GenericAgent包装器。

代码语言:javascript
运行
复制
instance HasAgent FwAD where
  type AgentVal FwAD a v = GenericAgent FwAD a v
  alg = genericAlg
  ($~) = genericAgentMap

instance CartesianAgent FwAD where
  alg1to2 = genericAlg1to2
  alg2to1 = genericAlg2to1
  alg2to2 = genericAlg2to2

instance PointAgent (GenericAgent FwAD) FwAD a x where
  point = genericPoint

instance ( Num v, AffineSpace v, Diff v ~ v, VectorSpace v, Scalar v ~ v
         , Scalar a ~ v )
      => Num (GenericAgent FwAD a v) where
  fromInteger = point . fromInteger
  (+) = genericAgentCombine . FwAD $ \(x,y) -> (x+y, \(dx,dy) -> dx+dy)
  (*) = genericAgentCombine . FwAD $ \(x,y) -> (x*y, \(dx,dy) -> y*dx+x*dy)
  abs = genericAgentMap . FwAD $ \x -> (abs x, \dx -> if x<0 then -dx else dx)
  ...
instance ( Fractional v, AffineSpace v, Diff v ~ v, VectorSpace v, Scalar v ~ v
         , Scalar a ~ v )
      => Fractional (GenericAgent FwAD a v) where
  ...
instance (...) => Floating (...) where
  ...

如果所有这些实例都已完成,并且可能需要一个简单的助手来提取结果。

代码语言:javascript
运行
复制
evalWithGrad :: FwAD Double Double -> Double -> (Double, Double)
evalWithGrad (FwAD f) x = case f x of
   (fx, df) -> (fx, df 1)

然后,您可以编写代码,例如

代码语言:javascript
运行
复制
> evalWithGrad (alg (\x -> x^2 + x) 3)
(12.0, 7.0)
> evalWithGrad (alg sin 0)
(0.0, 1.0)

在此基础上,这些代数表达式建立了FwAD箭头的组合,&&&“分割”数据流和***并行合成,即即使输入和最终结果是简单的Double,中间结果也将通过一个合适的元组类型得到。[我猜,这就是你标题问题的答案:在某种意义上,有向图表示为一个分支的合成链,原则上与你在这些S中发现的一样。]

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63279576

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档