2

所以这里已经很晚了,我的谷歌技能似乎让我失望了。我之前(一次又一次)在 SO 上找到了一些很好的回应,我认为你们可以提供帮助。

我有一个神经网络,我试图在本机 Objective-c 中运行。它有效,但它太慢了。这些网络不是循环的。每个网络我运行大约 20,000 次(128x80 次,或大约)。问题是这些网络实际上只是归结为数学函数(每个网络都是一个 4 维函数,以 x,y,dist(x,y) 和偏差作为输入,并输出 3 个值)。

我想要做的是将每个网络(仅一次)转换为函数调用,或在运行时在 Objective-C 中的代码块。

我该怎么做呢?我可以制作一大串需要执行的数学运算,但是我该如何去执行那个字符串,或者将字符串转换成一个代码块来执行呢?

再次,我深夜的搜索失败了,如果这已经得到回答,我很抱歉。任何帮助是极大的赞赏。

-保罗

编辑:啊哈!巨大的成功!将近 24 小时后,我有了工作代码,可以将具有多达 4 个输入的神经网络转换为单个 4 维函数。我在答案中使用了 Dave DeLong 建议的块方法。

对于任何想要关注我未来所做的事情的人,这里是我所做的(快速)细分(如果这是 stackoverflow 上的不正确礼仪,请原谅):首先,我为不同的块做了一些 typedef功能:

typedef CGFloat (^oneDFunction)(CGFloat x);
typedef CGFloat (^twoDFunction)(CGFloat x, CGFloat y);
typedef CGFloat (^threeDFunction)(CGFloat x, CGFloat y, CGFloat z);
typedef CGFloat (^fourDFunction)(CGFloat x, CGFloat y, CGFloat z, CGFloat w);

oneDFunction 采用 f(x) 的形式,twoD 是 f(x,y) 等。然后我制作了组合两个fourDFunction 块的函数(以及2 个oneD、2 个twoD 等,尽管这些不是必需的)。

fourDFunction (^combineFourD) (fourDFunction f1, fourDFunction f2) =
  ^(fourDFunction f1,     fourDFunction f2){
    fourDFunction blockToCopy = ^(CGFloat x, CGFloat y, CGFloat z, CGFloat w){
        return f1(x,y,z,w) + f2(x,y,z,w);
    };
    fourDFunction act = [blockToCopy copy];
    [f1 release];
    [f2 release];
    //Need to release act at some point
    return act;            
};

当然,我需要将激活函数应用于每个节点的fourD函数,对于每个节点,我需要乘以连接它的权重:

//for applying the activation function
fourDFunction (^applyOneToFourD)( oneDFunction f1, fourDFunction f2) = 
^(oneDFunction f1, fourDFunction f2){
    fourDFunction blockToCopy = ^(CGFloat x, CGFloat y, CGFloat z, CGFloat w){
        return f1(f2(x,y,z,w));
    };    

    fourDFunction act = [blockToCopy copy];
    [f1 release];
    [f2 release];

    //Need to release act at some point
    return act; 

};

//For applying the weight to the function
fourDFunction (^weightCombineFour) (CGFloat x, fourDFunction f1) =
 ^(CGFloat weight, fourDFunction f1)
{
    fourDFunction blockToCopy = ^(CGFloat x, CGFloat y, CGFloat z, CGFloat w){

        return weight*f1(x,y,z,w);
    };

    fourDFunction act = [blockToCopy copy];
    [f1 release];
    //[act release];
    //Need to release act at some point
   return act;

};

然后,对于网络中的每个节点,我简单地将激活函数应用于源神经元的四个函数之和乘以它们的连接权重。在组合完所有这些块之后,我从每个输出中获取了最终函数。因此,我的输出是输入的单独 4D 函数。

感谢您的帮助,这非常酷。

4

4 回答 4

4

您可以使用blocks来做到这一点。就像是:

//specify some parameters
int parameter1 = 42;
int parameter2 = 54;
//create your block
int (^myBlock)(int) = ^(int parameter3){
  return parameter1 * parameter2 * parameter3;
};
//copy the block off the stack
myBlock = [myBlock copy];
//stash the block somewhere so that you can pull it out later
[self saveBlockOffSomewhereElse:myBlock underName:@"myBlock"];
//balance the call to -copy
[myBlock release];

然后在别处...

int (^retrievedBlock)(int) = [self retrieveBlockWithName:@"myBlock"];
int theAnswer = retrievedBlock(2);  //theAnswer is 4536

如果您有一个代表一些数学要评估的字符串,您可以查看GCMathParser(快速但不可扩展)或我自己的DDMathParser(较慢但可扩展)。

于 2011-03-27T04:52:52.623 回答
3

你的想法不是很愚蠢。事实上,LLVM 旨在完成这类事情(生成代码、编译、链接、加载和运行),它甚至具有要链接的库和要使用的 API。

虽然你可以尝试将一堆块或原语拼凑在一起——一种你自己的虚拟机——但它会更慢并且可能需要更多的维护。您最终将不得不编写某种解析器,编写所有原始块,然后将它们拼凑在一起。

对于代码生成,很明显,你可能仍然需要一个解析器,但是生成的代码会快得多,因为你可以启动编译器上的优化器,只要你只生成一个非常大的代码文件,编译器的优化器会更加有效。

不过,我建议您生成程序,然后在应用程序外部运行它。这将阻止试图动态卸载代码的地狱。这也意味着如果生成的代码崩溃了,它并不会导致你的应用程序崩溃。

LLVM.org 有很多额外的细节。

(历史记录——皮克斯建模环境的一种早期形式是基于 TCL 的系统,从字面上看,它会发出数十万行大量模板化的 C++ 代码。)

于 2011-03-27T04:53:18.343 回答
1

再次为区块投票。如果你从一堆代表原始操作的块开始,你可以将它们组合成更大的块来代表复杂的功能。例如,您可以编写一个函数,将多个块作为参数,依次复制每个块并将其用作下一个块的第一个参数。函数的结果可以是表示数学函数的块。

也许我在这里说得太晚了,但似乎块引用其他块和维护状态的能力应该使它们非常适合组装操作。

于 2011-03-27T09:30:23.917 回答
1

这是另一种可能性:使用 OpenGL。

您在神经网络中执行的各种功能与 GPU 执行的功能非常相似。乘法/缩放、距离、sigmoid 等...您可以在位图中编码您的状态,将像素整形器生成为 ASCII,使用提供的库调用编译和链接它,然后生成带有新状态的输出“位图”。然后切换两个位图并再次迭代。

编写像素整形器并不像您想象的那么难。在基本情况下,您从输入位图/缓冲区中获得一个像素,然后您计算一个值以放入输出缓冲区。您还可以访问输入和输出缓冲区中的所有其他像素,作为您设置全局的任意参数的墙,包括可能仅用作任意数据向量的“纹理”位图。

现代 GPU 有多个管道,因此您可能会获得比本地 CPU 机器代码更好的性能。

于 2011-03-27T05:09:51.823 回答