Аналитичен начин за ускоряване на 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)) (с удвоено въздействие върху паметта за неразредена матрица от тип sparse) и 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.

Да, изчислявам exp по елементи, актуализирам горното уравнение, за да отразя това.

Още една редакция: Опитах се да бъда умен, но с малък успех:

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, 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 да бъде N1 x N2 x N3 ... x 81 по размер, че по някои от измеренията имам само един ненулев запис. Въпреки това, всички тези измерения са свити, така че това вече не е вярно за 26873856 на 81 матрица A. По-специално, това не е диагонална или блок-диагонална матрица. Коментарът за 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. (Мога да се отърва от преоформянето, като използвам 3D матрица за 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
Мисля, че трябва да преформулирате проблема си с реконструкцията на изображението, така че да избегнете преминаването между логаритмичната и естествената област. Това звучи като CT проблем - затихването следва експоненциален закон - и повечето CT алгоритми са измислили как да не правят това.   -  person Floris    schedule 01.05.2014


Отговори (1)


Компютрите всъщност не правят експоненти. Бихте си помислили, че го правят, но това, което правят, са полиномни приближения с висока точност.

Препратки:

Последната препратка изглеждаше доста добре. Може би трябваше да е първо.

Тъй като работите върху изображения, вероятно имате дискретен брой нива на интензитет (255 обикновено). Това може да позволи намалено вземане на проби или търсения, в зависимост от естеството на "А". Един от начините да проверите това е да направите нещо като следното за достатъчно представителна група от стойности на "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