如何在Objective-C中在运行时创建函数

时间:2011-03-27 04:35:03

标签: objective-c runtime neural-network dynamic-function

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

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

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

我该怎么做?我可以创建一大串需要执行的数学运算,但是如何执行该字符串,或者将字符串转换为代码块以便执行?

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

-Paul

编辑:啊哈!巨大的成功!差不多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;

};

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

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

4 个答案:

答案 0 :(得分: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(较慢但可扩展)。

答案 1 :(得分:3)

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

虽然你可以沿着试图拼凑一堆块或原语的路径 - 一种你自己的VM - 但它会更慢并且可能更多维护。您最终必须编写某种解析器,编写所有原始块,然后将它们拼凑在一起。

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

但我建议您生成程序,然后将外部运行到您的应用程序。这将阻止试图动态卸载代码的地狱。这也意味着如果生成的代码崩溃,则不会删除您的应用程序。

LLVM.org有一堆额外的细节。

(历史记录 - 皮克斯建模环境的一个早期形式是一个基于TCL的系统,它可以发出数十万行严格模板化的C ++代码。)

答案 2 :(得分:1)

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

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

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

现代GPU拥有多条流水线,因此您可能会获得比原生CPU机器代码更好的性能。

答案 3 :(得分:1)

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

也许由于时间已经很晚,我在这里说话很疯狂,但看起来块的能力引用其他块并保持状态应该会使它们非常适合于组装操作。