【问题标题】:How do I create a function at runtime in Objective-C如何在 Objective-C 运行时创建函数
【发布时间】:2011-03-27 04:35:02
【问题描述】:

所以现在已经很晚了,我的谷歌技能似乎让我失望了。我之前(一次又一次地)在 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 函数。

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

【问题讨论】:

  • 我唯一的愚蠢想法是在运行时生成一个源文件,调用编译器和链接器从中创建一个共享对象,然后使用 dlopen() 加载共享对象。通常可能无法将数据写入代码段并跳转到那里,除非它在堆栈上,这确实是不可靠的。
  • 生成 Objective-C 二进制文件的唯一方法是使用 Objective-C 二进制文件。当然,您可以通过将操作码写入内存并将控制权传递给它来自己生成机器码,但这条路充满了危险。

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


【解决方案1】:

您可以使用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和参数2呢?会是 int (^myBlock)(int parameter1, int parameter2)... 等等?这是正确的语法吗?
  • @Paul 是的,你可以让块接受参数,就像普通函数一样。
  • 感谢您的提示!我今天实现了块代码,并编辑了我的帖子以反映我所做的。非常非常有帮助。
  • @Paul fyi,你可以autorelease 一个块(关于你关于“需要在某个时候释放act”的评论)
【解决方案2】:

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

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

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

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

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

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

【讨论】:

  • 虽然我非常愿意按照您的建议去做,但这可能远高于我的 Objective-c 技能水平。我可能会采用块根,并不是说这不是一条完全有效的路径。另外,我应该提到我在 iPhone/iPad 环境中。我可以在我的应用程序外部运行吗?
  • 啊——iPhone/iPad。不,你不能这样做。戴夫的答案是要走的路。
【解决方案3】:

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

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

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

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

【讨论】:

  • 嗯,曾几何时,我写过 CUDA 代码来做一些非常相似的事情。我真的很喜欢这个想法,因为它可以大规模并行化。这在 iPhone/iPad 上还能正常工作吗?
【解决方案4】:

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

也许我是因为时间太晚了在这里说疯了,但似乎块引用其他块并保持状态的能力应该使它们非常适合组装操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-04
    • 2011-01-28
    • 2014-12-03
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    相关资源
    最近更新 更多