【问题标题】:F# Pick function based on input?F# Pick函数基于输入?
【发布时间】:2016-10-11 16:05:08
【问题描述】:

我有以下功能

let private sigmoid (z:float) =
    1.0 / (1.0 + exp(-z))
let private sigmoidM (z : Matrix<float>) : Matrix<float> =
    z.Map (fun x -> sigmoid(x))
let private sigmoidV (z:Vector<float>) =
    z.Map(fun x -> sigmoid(x))

我想只使用 sigmoid,它根据输入针对标量、向量或矩阵执行。

也就是说,这个函数需要非常高效,因为它位于循环的最关键部分。关于如何做到这一点的任何见解?随意张贴如何谨慎,如果它会很慢。

【问题讨论】:

  • 出于好奇,您为什么需要 Vector 结果。当我做我的神经网络时,我只需要Matrix。矩阵只使用了sigmoid,矩阵使用了Map函数,就像你做的那样,我在另一个答案中做了。
  • 在做神经网络时也需要注意。使用 CPU 和 GPU 之间的速度差异如此之大,以至于我只使用 CPU 来学习和测试代码,而对于任何严肃的工作只会使用 GPU,即使这样也只能使用预先编写的测试库,例如TensorFlow 我用过,CNTK 我没用过。简而言之,如果您正在学习神经网络的工作原理并在不到一小时的时间内获得结果,我不会担心;以后开始做大型项目时再担心。
  • 另外值得注意的是:如果您查看神经网络为什么使用 sigmoid 函数以及它们如何选择该函数和其他函数,您会发现在函数分析阶段进行了大量优化,即这就是 sigmoid 如此有用的原因。优化是在代码执行之外完成的。
  • 有什么理由不使用标准的 .NET 重载?

标签: performance f# sigmoid


【解决方案1】:

您可以使用标准的 .NET 重载:

open MathNet.Numerics.LinearAlgebra

type Sigmoid() = class end with
    static member sigmoid (z:float) = 1.0 / (1.0 + exp(-z))
    static member sigmoid (z:Matrix<float>) = z.Map (fun x -> Sigmoid.sigmoid(x))
    static member sigmoid (z:Vector<float>) = z.Map (fun x -> Sigmoid.sigmoid(x))

// Usage
let x = Sigmoid.sigmoid 4.3
let y = Sigmoid.sigmoid (matrix [[1.0; 2.0]; [3.0; 4.0]])
let z = Sigmoid.sigmoid (vector  [1.0; 2.0])

// Results
val x : float = 0.9866130822
val y : Matrix<float> = 
          DenseMatrix 2x2-Double
            0.731059  0.880797
            0.952574  0.982014
val z : Vector<float> = seq [0.7310585786; 0.880797078]

这不会影响性能,因为重载决议是在编译时完成的。

对标准的 .NET 重载不满意?不想将函数编码为成员?你想让它更通用(也接受 float32)并扩展到其他类型吗?

使用静态类型约束:

type Sigmoid() = class end with
    static member Sigmoid (_:Sigmoid, z:float  ) = 1.0  / (1.0  + exp(-z))
    static member Sigmoid (_:Sigmoid, z:float32) = 1.0f / (1.0f + exp(-z))

let inline _sigmoid (s:'Sigmoid) (x:'T) :'T =
    ((^T or ^Sigmoid) : (static member Sigmoid : 'Sigmoid * 'T -> 'T) (s, x))

let inline sigmoid x = _sigmoid (Sigmoid()) x 

type Sigmoid  with
    static member inline Sigmoid (_:Sigmoid, z:Matrix<'T>) = z.Map (fun x -> sigmoid x)
    static member inline Sigmoid (_:Sigmoid, z:Vector<'T>) = z.Map (fun x -> sigmoid x)

// Usage
let x = sigmoid 4.3
let y = sigmoid (matrix [[ 1.0; 2.0 ];[ 3.0; 4.0 ]])
let z = sigmoid (vector [ 1.0; 2.0 ])

let x' = sigmoid 4.3f
let y' = sigmoid (matrix [[1.0f; 2.0f];[ 3.0f; 4.0f]])
let z' = sigmoid (vector [ 1.0f; 2.0f])

更新

请注意,@TheInnerLight 在 cmets 中指出,对于您的特定 sigmoid 函数,您还可以编写:

let inline sigmoid z = 
    LanguagePrimitives.GenericOne / (LanguagePrimitives.GenericOne + exp(-z))

这适用于floatfloat32

这最终也适用于向量和矩阵,具体取决于它们的实现。

如果所有操作 negate、divide 和 exp 在这些类型上都是通用的并且它们都支持 GenericOne,那么这将是一个更好的解决方案。

不幸的是,截至今天,MathNet 没有以这种方式为 Matrix 和 Vector 实现 GenericOneexp

【讨论】:

  • 您不需要围绕创建采用各种数字类型的Sigmoid 函数的所有额外基础设施。你可以写let inline sigmoid z = LanguagePrimitives.GenericOne / (LanguagePrimitives.GenericOne + exp(-z))
  • @TheInnerLight 对于这个sigmoid 函数的特定情况,这应该是正确的方法,因为1exp 已经是所需类型的泛型。所以对于这种情况来说已经足够好了。您应该将其添加为答案。
  • 不,这只是对您的答案的微小修改,使用不同的数字类型只是您添加的一些额外信息,而不是原始问题的一部分。我只是建议它,所以如果你愿意,你可以把它放在你的答案中。
  • @TheInnerLight 好的,我添加了作为最后的说明。谢谢!
  • 你们中的任何一个人使用LanguagePrimitivesMathNet Numerics 测试过版本吗?
猜你喜欢
  • 1970-01-01
  • 2013-04-16
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多