Как создать функцию во время выполнения в Objective-C - PullRequest
2 голосов
/ 27 марта 2011

Итак, уже поздно, и мои навыки работы с Google, похоже, подводят меня. Я нашел несколько хороших ответов на SO раньше (снова и снова), я думал, что вы, ребята, могли бы помочь.

У меня есть нейронная сеть, которую я пытаюсь запустить в native-c. Это работает, но это слишком медленно. Эти сети не повторяются. Каждую сеть я запускаю около 20000 раз (128х80 раз или около того). Проблема заключается в том, что эти сети на самом деле сводятся к математическим функциям (каждая сеть представляет собой четырехмерную функцию, принимающую x, y, dist (x, y) и смещение в качестве входных данных и выводящую 3 значения).

Что я хочу сделать, так это преобразовать каждую сеть (только один раз) в вызов функции или блок кода во время выполнения в target-c.

Как мне это сделать? Я мог бы сделать большую строку математических операций, которые должны быть выполнены, но как мне выполнить эту строку или преобразовать строку в блок кода для выполнения?

Опять же, мой поздний ночной поиск не дал мне результата, извините, если на этот вопрос уже был дан ответ. Любая помощь очень ценится.

-Поль

Редактировать: Ага! Огромный успех! Почти через 24 часа у меня есть рабочий код, чтобы превратить нейронную сеть с 4 входами в одну 4-мерную функцию. Я использовал блочный метод, предложенный Дейвом Делонгом в ответах.

Для любого, кто когда-либо захочет следить за тем, что я сделал в будущем, вот (быстрый) анализ того, что я сделал (извините, если это неправильный этикет в 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 голосов
/ 27 марта 2011

Вы можете сделать это с помощью блоков .Что-то вроде:

//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 (медленнее, но расширяемый).

3 голосов
/ 27 марта 2011

Твоя идея не очень глупая.Фактически, LLVM предназначен для выполнения в точности подобных вещей (генерации кода, компиляции, связывания, загрузки и запуска), и даже имеет библиотеки для ссылок и API для использования.

В то время как вы могли бы идти по пути, пытаясь собрать воедино кучу блоков или примитивов - своего рода виртуальную машину - это будет медленнее и, вероятно, потребует больше обслуживания.В конечном итоге вам придется написать какой-то синтаксический анализатор, написать все примитивные блоки, а затем сложить их все вместе.

Для генерации кода вам, вероятно, все еще понадобится парсер, очевидно, норезультирующий код будет намного намного быстрее, потому что вы можете запустить оптимизатор на компиляторе и, если вы сгенерируете только один действительно большой файл кода, оптимизатор компилятора будет еще более эффективным.

Я бы предложил, однако, чтобы вы сгенерировали свою программу, а затем запустили ее извне для вашего приложения.Это предотвратит тот ад, который пытается динамически выгружать код.Это также означает, что в случае сбоя сгенерированного кода ваше приложение не будет удалено.

LLVM.org содержит множество дополнительных сведений.

(Историческая справка - одна ранняя формаСреда моделирования Pixar представляла собой систему на основе TCL, которая в буквальном смысле генерировала бы сотни тысяч строк сильно шаблонизированного кода C ++.)

1 голос
/ 27 марта 2011

Еще один голос за блоки.Если вы начнете с набора блоков, представляющих примитивные операции, вы можете объединить их в более крупные блоки, представляющие сложные функции.Например, вы можете написать функцию, которая принимает несколько блоков в качестве параметров, копирует каждый из них по очереди и использует ее в качестве первого параметра для следующего блока.Результатом этой функции может быть блок, представляющий математическую функцию.

Возможно, я схожу с ума здесь из-за позднего часа, но похоже, что способность блоков ссылаться на другие блоки и поддерживатьштат должен сделать их очень хорошими для операций сборки.

1 голос
/ 27 марта 2011

Вот еще одна возможность: использовать OpenGL.

Виды функций, которые вы выполняете в нейронной сети, очень похожи на те, которые выполняются графическими процессорами.умножение / масштабирование, расстояние, сигмоиды и т. д. Вы можете закодировать свое состояние в растровом изображении, сгенерировать формирователь пикселей как ASCII, скомпилировать и связать его с помощью предоставленных библиотечных вызовов, а затем сгенерировать выходной «растровый рисунок» с новым состоянием.Затем переключите два растровых изображения и повторите итерацию.

Написание формирователя пикселей не так сложно, как вы можете себе представить.В базовом случае вам дается пиксель из входного растрового изображения / буфера, и вы вычисляете значение для помещения в выходной буфер.У вас также есть доступ ко всем остальным пикселям во входном и выходном буферах, в качестве стены в качестве произвольных параметров, которые вы задаете глобальные, включая «текстурные» растровые изображения, которые могут служить просто произвольным вектором данных.

Современные графические процессоры имеют несколькоконвейеры, так что вы, вероятно, получите гораздо лучшую производительность, чем даже собственный машинный код процессора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...