Аналитический способ ускорения exp(A*x) в MATLAB

Мне нужно повторно вычислить f(x)=exp(A*x) для крошечного переменного вектора-столбца x и огромной постоянной матрицы A (много строк, мало столбцов). Другими словами, x мало, а A*x много. Размеры моей проблемы таковы, что A*x занимает примерно столько же времени выполнения, сколько и часть exp().

Помимо расширения Тейлора и предварительного расчета диапазона значений exp(y) (при условии, что известен диапазон y значений A*x), который мне не удалось значительно ускорить (при сохранении точности) по сравнению с тем, что MATLAB делает сам по себе , Я думаю об аналитической переформулировке задачи, чтобы иметь возможность предварительно вычислить некоторые значения.

Например, я обнаружил, что exp(A*x)_i = exp(\sum_j A_ij x_j) = \prod_j exp(A_ij x_j) = \prod_j exp(A_ij)^x_j

Это позволило бы мне предварительно вычислить exp(A) один раз, но требуемое возведение в степень в цикле так же затратно, как и исходный вызов функции exp(), а умножения (\prod) должны выполняться дополнительно.

Есть ли какая-нибудь другая идея, которой я мог бы следовать, или решения в MATLAB, которые я мог пропустить?

Изменить: некоторые подробности

A имеет размер 26873856 на 81 (да, он такой огромный), поэтому x равен 81 на 1. nnz(A) / numel(A) равен 0.0012, nnz(A*x) / numel(A*x) равен 0.0075. Я уже использую разреженную матрицу для представления A, однако exp() разреженной матрицы больше не является разреженной. Так что на самом деле я сохраняю x неразреженным и вычисляю exp(full(A*x)), которое оказалось таким же быстрым/медленным, как full(exp(A*x)) (я думаю, что A*x в любом случае не является разреженным, так как x не является разреженным.) exp(full(A*sparse(x))) — это способ получить редкий A*x, но медленнее. Еще более медленные варианты — это exp(A*sparse(x)) (с удвоенной нагрузкой на память для неразреженной матрицы разреженного типа) и full(exp(A*sparse(x)) (что снова дает неразреженный результат).

sx = sparse(x);
tic, for i = 1 : 10, exp(full(A*x)); end, toc
tic, for i = 1 : 10, full(exp(A*x)); end, toc
tic, for i = 1 : 10, exp(full(A*sx)); end, toc
tic, for i = 1 : 10, exp(A*sx); end, toc
tic, for i = 1 : 10, full(exp(A*sx)); end, toc

Elapsed time is 1.485935 seconds.
Elapsed time is 1.511304 seconds.
Elapsed time is 2.060104 seconds.
Elapsed time is 3.194711 seconds.
Elapsed time is 4.534749 seconds.

Да, я рассчитываю опыт поэлементно, я обновляю приведенное выше уравнение, чтобы отразить это.

Еще одно редактирование: я пытался быть умным, но без особого успеха:

tic, for i = 1 : 10, B = exp(A*x); end, toc
tic, for i = 1 : 10, C = 1 + full(spfun(@(x) exp(x) - 1, A * sx)); end, toc
tic, for i = 1 : 10, D = 1 + full(spfun(@(x) exp(x) - 1, A * x)); end, toc
tic, for i = 1 : 10, E = 1 + full(spfun(@(x) exp(x) - 1, sparse(A * x))); end, toc
tic, for i = 1 : 10, F = 1 + spfun(@(x) exp(x) - 1, A * sx); end, toc
tic, for i = 1 : 10, G = 1 + spfun(@(x) exp(x) - 1, A * x); end, toc
tic, for i = 1 : 10, H = 1 + spfun(@(x) exp(x) - 1, sparse(A * x)); end, toc

Elapsed time is 1.490776 seconds.
Elapsed time is 2.031305 seconds.
Elapsed time is 2.743365 seconds.
Elapsed time is 2.818630 seconds.
Elapsed time is 2.176082 seconds.
Elapsed time is 2.779800 seconds.
Elapsed time is 2.900107 seconds.

person bers    schedule 24.04.2014    source источник
comment
Я пробовал с A = rand(10000, 10). Использование expA = exp(A); prod(bsxfun(@power, expA, x'), 2) действительно намного медленнее. Выполнение exp(A*x) в функции mex тоже мало помогает, даже при использовании одинарной точности. Каков размер A для вашего использования?   -  person buzjwa    schedule 24.04.2014
comment
Мое внутреннее чувство подсказывало, что это можно упростить, взяв сингулярное разложение A или около того, но, подумав еще немного, я не понимаю, как это сделать. Я думаю, что самый простой способ рассуждать - это принять предел, что x является скаляром, а A вектором. Я не вижу никакого способа, как это можно ускорить.   -  person Bas Swinckels    schedule 24.04.2014
comment
Я склонен согласиться с предыдущими комментариями. Если многие элементы равны нулю, так что A можно считать разреженным, это может ускорить процесс. Но вопрос: вы же не пытаетесь вычислить матричную экспоненту, верно?   -  person patrik    schedule 24.04.2014
comment
не могли бы вы объяснить, почему это считается медленным? что вам нужно сделать, чтобы 0,15 секунды на расчет были слишком медленными?   -  person bla    schedule 25.04.2014
comment
@natan: Это какой-то новый процесс реконструкции медицинских изображений, который в основном пытается инвертировать что-то вроде y = exp(Ax)*x. При итеративном поиске решения мне часто (несколько сотен тысяч раз) нужно вычислить \hat{y} из некоторой текущей оценки \hat{x}. И поскольку exp(Ax) составляет около 30-50% от общего времени вычислений, я подумал, что имеет смысл ускорить его :)   -  person bers    schedule 25.04.2014
comment
хорошо, значит x меняется, а A нет? это происходит в шагах компрессионного зондирования?   -  person bla    schedule 25.04.2014
comment
@natan: да, x меняется и не является разреженным, A постоянно и разреженно. Однако на самом деле это не CS: x представляет собой реконструируемое изображение (преобразованное в вектор), A относится к матрице системы визуализации (или, скорее, exp(Ax)). Таким образом, A не является разрежающим преобразованием или чем-то еще. Кроме того, из-за размера A, Ax имеет намного больше записей, чем x, несмотря на разреженность A, и поэтому имеет exp(A*x)*x.   -  person bers    schedule 25.04.2014
comment
Имеет ли A какую-либо особую структуру?   -  person nojka_kruva    schedule 28.04.2014
comment
Как насчет того, чтобы вместо этого решить w:=ln(y)=A*x+ln(x) и взять y=exp(w) в конце? 81 логарифм на шаг вместо 81*26*10**6 раз, операция exp() может быть быстрее.   -  person Horst Grünbusch    schedule 28.04.2014
comment
A имеет какую-то особую структуру: во-первых, она разреженная (см. выше). Во-вторых, я знаю, что если я изменю форму A так, чтобы она была размером N1 x N2 x N3 ... x 81, то по некоторым измерениям у меня будет только одна ненулевая запись. Однако все эти измерения свернуты, так что это уже не верно для матрицы A 26873856 на 81. В частности, это не диагональная или блочно-диагональная матрица. Комментарий о ln(y) = Ax + ln(x) выглядит интересно. Я не уверен, что ln(Bx) = ln(B) + ln(x) для матрично-векторных произведений (посмотрите на размерности), но, возможно, эта идея может дать что-то полезное позже.   -  person bers    schedule 29.04.2014
comment
@bers: я предположил, что вы имели в виду в своем комментарии в 21:02 в нотации Matlab y=exp(A*x) .* x. Правильно или неправильно поняли?   -  person Horst Grünbusch    schedule 29.04.2014
comment
Это может быть не к делу, но, как вы сказали, вы хотели что-то инвертировать: если вы пытаетесь решить систему уравнений, обязательно рассмотрите возможность использования матричного деления, а не обратного, как описано в doc.   -  person Dennis Jaheruddin    schedule 29.04.2014
comment
@HorstGrünbusch: Неправильно понято :) exp(Ax)*x действительно представляет собой два умножения матрицы на вектор; exp(Ax) изменяется между ними, чтобы быть N на 81. (Я могу избавиться от изменения формы, используя трехмерную матрицу для A, которая равна N на 81 на 81, но тогда разреженность и матричные умножения становятся более сложно в MATLAB.) Единственная поэлементная операция - это exp.   -  person bers    schedule 29.04.2014
comment
Затем w(1:N,j)=A*x + log(x(j)) и у вас снова 82*N вычислений функций. ОК, последнее предложение: напишите и скомпилируйте подпрограмму на Fortran95. Это быстро с циклами, простой синтаксис.   -  person Horst Grünbusch    schedule 29.04.2014
comment
Я думаю, вам нужно переформулировать задачу разведки изображений, чтобы избежать перехода между логарифмической и естественной областью. Это похоже на проблему КТ — затухание подчиняется экспоненциальному закону — и большинство алгоритмов КТ придумали, как этого не делать.   -  person Floris    schedule 01.05.2014


Ответы (1)


Компьютеры на самом деле не делают экспоненты. Вы могли бы подумать, что они это делают, но то, что они делают, — это высокоточные полиномиальные приближения.

Использованная литература:

Последнее упоминание выглядело довольно красиво. Возможно, это должно было быть первым.

Поскольку вы работаете с изображениями, у вас, вероятно, есть дискретное количество уровней интенсивности (обычно 255). Это может позволить уменьшить выборку или поиск, в зависимости от характера «A». Один из способов проверить это — сделать что-то вроде следующего для достаточно репрезентативной группы значений «x»:

y=Ax
cdfplot(y(:))

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

Мои подходы включают:

  • посмотрите на альтернативные формулировки exp (x), которые имеют более низкую точность, но более высокую скорость
  • рассмотрите поиск в таблице, если у вас недостаточно уровней «x»
  • рассмотрите комбинацию интерполяции и поиска по таблице, если у вас «слишком много» уровней для поиска по таблице
  • рассмотреть единый поиск (или альтернативную формулировку) на основе сегментированного режима. Если вы знаете, что это кость, и ищете вену, то, возможно, следует применить менее дорогостоящую обработку данных.

Теперь я должен спросить себя, почему вы живете в таком количестве итераций exp(A*x)*x, и я думаю, что вы можете переключаться между областью частоты/волнового числа и областью времени/пространства. Вы также можете иметь дело с вероятностями, используя exp(x) в качестве основы и занимаясь байесовской забавой. Я не знаю, является ли exp(x) хорошим сопряженным априором, поэтому я собираюсь использовать материал Фурье.

Другие варианты: - рассмотрите возможность использования fft, fft2 или fftn с учетом ваших матриц - они быстрые и могут сделать часть того, что вы ищете.

Я уверен, что существует более дальний вариант домена для следующего:

Возможно, вам удастся совместить поиск с вычислением, используя матрицу Вудбери. Я должен подумать об этом, чтобы быть уверенным. (ссылка) В какой-то момент я понял, что все, что имеет значение (CFD, FEA, FFT), все об инверсии матриц, но я уже забыл некоторые детали.

Теперь, если вы живете в MatLab, вы можете подумать об использовании «кодера», который преобразует код MatLab в c-код. Каким бы интересным ни был интерпретатор, хороший c-компилятор может быть намного быстрее. Мнемоника (надеюсь, не слишком амбициозная), которую я использую, показана здесь: ссылка, начиная с 13:49. Это очень просто, но оно показывает разницу между каноническим интерпретируемым языком (python) и его скомпилированной версией (cython/c).

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

У вас может не быть хорошего способа сделать это на обычном оборудовании, но вы можете подумать о чем-то вроде GPGPU. CUDA и его аналоги имеют массовые параллельные операции, которые позволяют существенно ускориться за счет нескольких видеокарт. У вас могут быть тысячи «ядер» (преувеличенных конвейеров), выполняющих работу нескольких ALU, и если задание правильно распараллеливается (как это выглядит), то оно может быть выполнено НАМНОГО быстрее.

РЕДАКТИРОВАТЬ:

Я думал о Eureqa. Один из вариантов, который я бы рассмотрел, если бы у меня было какое-то «большое железо» для разработки, но не для производства, — это использовать их продукт Eureqa, чтобы получить достаточно быстрое и достаточно точное приближение.

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

Теперь, если бы это был я, я бы хотел получить «пятерку» из показателя степени. Мне интересно, можете ли вы взглянуть на матрицу собственных векторов 81x81 и «x» и немного подумать о линейной алгебре и о том, в какое пространство вы проецируете свои векторы. Есть ли способ сделать функцию, которая выглядит следующим образом:

f(x) = B2 * exp( B1 * x)

так что

B1 * x

намного меньше ранга, чем ваш нынешний

Ax.

person EngrStudent    schedule 05.05.2014